From c166a9f580b80e43cc628be9c6062e54d7f96d94 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 8 Apr 2024 15:06:39 -0600 Subject: [PATCH] Add support for async/streams/futures This adds support for loading, compiling, linking, and running components which use the [Async ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md) along with the [`stream`, `future`, and `error-context`](https://github.com/WebAssembly/component-model/pull/405) types. It also adds support for generating host bindings such that multiple host functions can be run concurrently with guest tasks -- without monopolizing the `Store`. See the [implementation RFC](https://github.com/bytecodealliance/rfcs/pull/38) for details, as well as [this repo](https://github.com/dicej/component-async-demo) containing end-to-end smoke tests. Signed-off-by: Joel Dice fix clippy warnings and bench/fuzzing errors Signed-off-by: Joel Dice revert atomic.wit whitespace change Signed-off-by: Joel Dice fix build when component-model disabled Signed-off-by: Joel Dice bless component-macro expected output Signed-off-by: Joel Dice fix no-std build error Signed-off-by: Joel Dice fix build with --no-default-features --features runtime,component-model Signed-off-by: Joel Dice partly fix no-std build It's still broken due to the use of `std::collections::HashMap` in crates/wasmtime/src/runtime/vm/component.rs. I'll address that as part of the work to avoid exposing global task/future/stream/error-context handles to guests. Signed-off-by: Joel Dice maintain per-instance tables for futures, streams, and error-contexts Signed-off-by: Joel Dice refactor task/stream/future handle lifting/lowering This addresses a couple of issues: - Previously, we were passing task/stream/future/error-context reps directly to instances while keeping track of which instance had access to which rep. That worked fine in that there was no way to forge access to inaccessible reps, but it leaked information about what other instances were doing. Now we maintain per-instance waitable and error-context tables which map the reps to and from the handles which the instance sees. - The `no_std` build was broken due to use of `HashMap` in `runtime::vm::component`, which is now fixed. Note that we use one single table per instance for all tasks, streams, and futures. This is partly necessary because, when async events are delivered to the guest, it wouldn't have enough context to know which stream or future we're talking about if each unique stream and future type had its own table. So at minimum, we need to use the same table for all streams (regardless of payload type), and likewise for futures. Also, per https://github.com/WebAssembly/component-model/issues/395#issuecomment-2486783242, the plan is to move towards a shared table for all resource types as well, so this moves us in that direction. Signed-off-by: Joel Dice fix wave breakage due to new stream/future/error-context types Signed-off-by: Joel Dice switch wasm-tools to v1.220.0-based branch Signed-off-by: Joel Dice check `task.return` type at runtime We can't statically verify a given call to `task.return` corresponds to the expected core signature appropriate for the currently running task, so we must do so at runtime. In order to make that check efficient, we intern the types. My initial plan was to use `ModuleInternedTypeIndex` and/or `VMSharedTypeIndex` for interning, but that got hairy with WasmGC considerations, so instead I added new fields to `ComponentTypes` and `ComponentTypesBuilder`. Signed-off-by: Joel Dice add `TypedFunc::call_concurrent` and refine stream/future APIs This implements what I proposed in https://github.com/dicej/rfcs/blob/component-async/accepted/component-model-async.md#wasmtime. Specifically, it adds: - A new `Promise` type, useful for working with concurrent operations that require access to a `Store` to make progress. - A new `PromisesUnordered` type for `await`ing multiple promises concurrently -`TypedFunc::call_concurrent` (which returns a `Promise`), allowing multiple host->guest calls to run concurrently on the same instance. - Updated `{Stream|Future}{Writer|Reader}` APIs which use `Promise` The upshot is that the embedder can now ergonomically manage arbitrary numbers of concurrent operations. Previously, this was a lot more difficult to do without accidentally starving some of the operations due to another one monopolizing the `Store`. Finally, this includes various refactorings and fixes for bugs exposed by the newer, more versatile APIs. Signed-off-by: Joel Dice clean up verbosity in component/func.rs Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice implement stream/future read/write cancellation This required a somewhat viral addition of `Send` and `Sync` bounds for async host function closure types, unfortunately. Signed-off-by: Joel Dice add `Func::call_concurrent` and `LinkerInstance::func_new_concurrent` Signed-off-by: Joel Dice dynamic API support for streams/futures/error-contexts Signed-off-by: Joel Dice support callback-less (AKA stackful) async lifts Signed-off-by: Joel Dice fix `call_host` regression Signed-off-by: Joel Dice add component model async end-to-end tests I've ported these over from https://github.com/dicej/component-async-demo Signed-off-by: Joel Dice fix test regressions and clippy warnings Signed-off-by: Joel Dice satisfy clippy Signed-off-by: Joel Dice --- Cargo.lock | 369 +-- Cargo.toml | 28 +- benches/call.rs | 3 +- crates/component-macro/Cargo.toml | 1 + crates/component-macro/src/bindgen.rs | 48 +- crates/component-macro/tests/expanded/char.rs | 29 +- .../tests/expanded/char_async.rs | 25 +- .../tests/expanded/char_tracing_async.rs | 25 +- .../tests/expanded/conventions.rs | 79 +- .../tests/expanded/conventions_async.rs | 45 +- .../expanded/conventions_tracing_async.rs | 45 +- .../tests/expanded/dead-code.rs | 31 +- .../tests/expanded/dead-code_async.rs | 33 +- .../tests/expanded/dead-code_tracing_async.rs | 33 +- .../tests/expanded/direct-import.rs | 7 +- .../tests/expanded/direct-import_async.rs | 9 +- .../expanded/direct-import_tracing_async.rs | 9 +- .../component-macro/tests/expanded/empty.rs | 7 +- .../tests/expanded/empty_async.rs | 9 +- .../tests/expanded/empty_tracing_async.rs | 9 +- .../component-macro/tests/expanded/flags.rs | 54 +- .../tests/expanded/flags_async.rs | 35 +- .../tests/expanded/flags_tracing_async.rs | 35 +- .../component-macro/tests/expanded/floats.rs | 39 +- .../tests/expanded/floats_async.rs | 29 +- .../tests/expanded/floats_tracing_async.rs | 29 +- .../tests/expanded/function-new.rs | 12 +- .../tests/expanded/function-new_async.rs | 11 +- .../expanded/function-new_tracing_async.rs | 11 +- .../tests/expanded/host-world.rs | 7 +- .../tests/expanded/host-world_async.rs | 9 +- .../expanded/host-world_tracing_async.rs | 9 +- .../tests/expanded/integers.rs | 109 +- .../tests/expanded/integers_async.rs | 57 +- .../tests/expanded/integers_tracing_async.rs | 57 +- .../component-macro/tests/expanded/lists.rs | 164 +- .../tests/expanded/lists_async.rs | 79 +- .../tests/expanded/lists_tracing_async.rs | 79 +- .../tests/expanded/many-arguments.rs | 29 +- .../tests/expanded/many-arguments_async.rs | 25 +- .../expanded/many-arguments_tracing_async.rs | 25 +- .../tests/expanded/multi-return.rs | 44 +- .../tests/expanded/multi-return_async.rs | 31 +- .../expanded/multi-return_tracing_async.rs | 31 +- .../tests/expanded/multiversion.rs | 41 +- .../tests/expanded/multiversion_async.rs | 37 +- .../expanded/multiversion_tracing_async.rs | 37 +- .../component-macro/tests/expanded/path1.rs | 19 +- .../tests/expanded/path1_async.rs | 21 +- .../tests/expanded/path1_tracing_async.rs | 21 +- .../component-macro/tests/expanded/path2.rs | 19 +- .../tests/expanded/path2_async.rs | 21 +- .../tests/expanded/path2_tracing_async.rs | 21 +- .../component-macro/tests/expanded/records.rs | 74 +- .../tests/expanded/records_async.rs | 43 +- .../tests/expanded/records_tracing_async.rs | 43 +- .../component-macro/tests/expanded/rename.rs | 31 +- .../tests/expanded/rename_async.rs | 33 +- .../tests/expanded/rename_tracing_async.rs | 33 +- .../tests/expanded/resources-export.rs | 63 +- .../tests/expanded/resources-export_async.rs | 41 +- .../resources-export_tracing_async.rs | 41 +- .../tests/expanded/resources-import.rs | 103 +- .../tests/expanded/resources-import_async.rs | 99 +- .../resources-import_tracing_async.rs | 99 +- .../tests/expanded/share-types.rs | 33 +- .../tests/expanded/share-types_async.rs | 35 +- .../expanded/share-types_tracing_async.rs | 35 +- .../tests/expanded/simple-functions.rs | 49 +- .../tests/expanded/simple-functions_async.rs | 33 +- .../simple-functions_tracing_async.rs | 33 +- .../tests/expanded/simple-lists.rs | 39 +- .../tests/expanded/simple-lists_async.rs | 29 +- .../expanded/simple-lists_tracing_async.rs | 29 +- .../tests/expanded/simple-wasi.rs | 31 +- .../tests/expanded/simple-wasi_async.rs | 33 +- .../expanded/simple-wasi_tracing_async.rs | 33 +- .../tests/expanded/small-anonymous.rs | 24 +- .../tests/expanded/small-anonymous_async.rs | 23 +- .../expanded/small-anonymous_tracing_async.rs | 23 +- .../tests/expanded/smoke-default.rs | 12 +- .../tests/expanded/smoke-default_async.rs | 11 +- .../expanded/smoke-default_tracing_async.rs | 11 +- .../tests/expanded/smoke-export.rs | 12 +- .../tests/expanded/smoke-export_async.rs | 11 +- .../expanded/smoke-export_tracing_async.rs | 11 +- .../component-macro/tests/expanded/smoke.rs | 16 +- .../tests/expanded/smoke_async.rs | 21 +- .../tests/expanded/smoke_tracing_async.rs | 21 +- .../component-macro/tests/expanded/strings.rs | 34 +- .../tests/expanded/strings_async.rs | 27 +- .../tests/expanded/strings_tracing_async.rs | 27 +- .../tests/expanded/unstable-features.rs | 25 +- .../tests/expanded/unstable-features_async.rs | 27 +- .../unstable-features_tracing_async.rs | 27 +- .../tests/expanded/unversioned-foo.rs | 19 +- .../tests/expanded/unversioned-foo_async.rs | 21 +- .../expanded/unversioned-foo_tracing_async.rs | 21 +- .../tests/expanded/use-paths.rs | 52 +- .../tests/expanded/use-paths_async.rs | 57 +- .../tests/expanded/use-paths_tracing_async.rs | 57 +- .../tests/expanded/variants.rs | 129 +- .../tests/expanded/variants_async.rs | 65 +- .../tests/expanded/variants_tracing_async.rs | 65 +- crates/component-macro/tests/expanded/wat.rs | 7 +- .../tests/expanded/wat_async.rs | 9 +- .../tests/expanded/wat_tracing_async.rs | 9 +- .../tests/expanded/worlds-with-types.rs | 24 +- .../tests/expanded/worlds-with-types_async.rs | 23 +- .../worlds-with-types_tracing_async.rs | 23 +- crates/cranelift/Cargo.toml | 1 + crates/cranelift/src/compiler/component.rs | 1072 ++++++++- crates/environ/examples/factc.rs | 4 + crates/environ/src/component.rs | 4 + crates/environ/src/component/dfg.rs | 232 +- crates/environ/src/component/info.rs | 295 ++- crates/environ/src/component/translate.rs | 259 +- .../environ/src/component/translate/adapt.rs | 20 +- .../environ/src/component/translate/inline.rs | 326 +++ crates/environ/src/component/types.rs | 102 +- crates/environ/src/component/types_builder.rs | 178 +- .../src/component/types_builder/resources.rs | 5 + .../src/component/vmcomponent_offsets.rs | 269 ++- crates/environ/src/fact.rs | 191 +- crates/environ/src/fact/signature.rs | 99 +- crates/environ/src/fact/trampoline.rs | 618 ++++- crates/environ/src/trap_encoding.rs | 7 +- .../fuzzing/src/generators/component_types.rs | 31 +- crates/misc/component-async-tests/Cargo.toml | 23 + .../component-async-tests/http/Cargo.toml | 9 + .../component-async-tests/http/src/lib.rs | 565 +++++ crates/misc/component-async-tests/src/lib.rs | 1139 +++++++++ .../wit/deps/http/handler.wit | 17 + .../wit/deps/http/proxy.wit | 6 + .../wit/deps/http/types.wit | 424 ++++ .../misc/component-async-tests/wit/test.wit | 86 + crates/misc/component-test-util/src/lib.rs | 22 +- crates/test-programs/Cargo.toml | 3 + crates/test-programs/artifacts/Cargo.toml | 1 + crates/test-programs/artifacts/build.rs | 18 +- crates/test-programs/src/bin/api_proxy.rs | 4 +- .../src/bin/async_backpressure_callee.rs | 36 + .../src/bin/async_backpressure_caller.rs | 81 + .../test-programs/src/bin/async_http_echo.rs | 68 + .../src/bin/async_http_middleware.rs | 161 ++ crates/test-programs/src/bin/async_poll.rs | 102 + .../src/bin/async_round_trip_stackful.rs | 150 ++ .../src/bin/async_round_trip_stackless.rs | 26 + .../src/bin/async_round_trip_synchronous.rs | 25 + .../src/bin/async_round_trip_wait.rs | 35 + .../src/bin/async_transmit_callee.rs | 77 + .../src/bin/async_transmit_caller.rs | 166 ++ .../src/bin/async_yield_callee.rs | 27 + .../src/bin/async_yield_caller.rs | 62 + .../http_outbound_request_invalid_header.rs | 28 +- crates/wasi-config/Cargo.toml | 2 +- crates/wasi-keyvalue/src/lib.rs | 2 +- crates/wasmtime/Cargo.toml | 10 + crates/wasmtime/src/config.rs | 11 + crates/wasmtime/src/engine/serialization.rs | 9 + .../src/runtime/component/component.rs | 1 + .../src/runtime/component/concurrent.rs | 2085 +++++++++++++++++ .../concurrent/futures_and_streams.rs | 2007 ++++++++++++++++ .../component/concurrent/ready_chunks.rs | 59 + .../src/runtime/component/concurrent/table.rs | 316 +++ crates/wasmtime/src/runtime/component/func.rs | 534 ++++- .../src/runtime/component/func/host.rs | 391 +++- .../src/runtime/component/func/options.rs | 18 +- .../src/runtime/component/func/typed.rs | 268 ++- .../src/runtime/component/instance.rs | 61 +- .../wasmtime/src/runtime/component/linker.rs | 139 +- .../src/runtime/component/matching.rs | 11 +- crates/wasmtime/src/runtime/component/mod.rs | 481 ++++ .../wasmtime/src/runtime/component/storage.rs | 27 +- .../wasmtime/src/runtime/component/types.rs | 100 +- .../wasmtime/src/runtime/component/values.rs | 54 +- crates/wasmtime/src/runtime/store.rs | 21 + crates/wasmtime/src/runtime/vm/component.rs | 523 ++++- .../src/runtime/vm/component/libcalls.rs | 43 +- .../src/runtime/vm/component/states.rs | 126 + .../runtime/vm/instance/allocator/pooling.rs | 1 + crates/wasmtime/src/runtime/vm/interpreter.rs | 2 +- crates/wasmtime/src/runtime/wave/component.rs | 10 +- crates/wast/src/component.rs | 3 + crates/wit-bindgen/Cargo.toml | 1 + crates/wit-bindgen/src/lib.rs | 733 ++++-- crates/wit-bindgen/src/rust.rs | 17 +- crates/wit-bindgen/src/types.rs | 11 +- tests/all/component_model/dynamic.rs | 2 +- tests/all/pooling_allocator.rs | 2 +- 190 files changed, 17139 insertions(+), 2166 deletions(-) create mode 100644 crates/misc/component-async-tests/Cargo.toml create mode 100644 crates/misc/component-async-tests/http/Cargo.toml create mode 100644 crates/misc/component-async-tests/http/src/lib.rs create mode 100644 crates/misc/component-async-tests/src/lib.rs create mode 100644 crates/misc/component-async-tests/wit/deps/http/handler.wit create mode 100644 crates/misc/component-async-tests/wit/deps/http/proxy.wit create mode 100644 crates/misc/component-async-tests/wit/deps/http/types.wit create mode 100644 crates/misc/component-async-tests/wit/test.wit create mode 100644 crates/test-programs/src/bin/async_backpressure_callee.rs create mode 100644 crates/test-programs/src/bin/async_backpressure_caller.rs create mode 100644 crates/test-programs/src/bin/async_http_echo.rs create mode 100644 crates/test-programs/src/bin/async_http_middleware.rs create mode 100644 crates/test-programs/src/bin/async_poll.rs create mode 100644 crates/test-programs/src/bin/async_round_trip_stackful.rs create mode 100644 crates/test-programs/src/bin/async_round_trip_stackless.rs create mode 100644 crates/test-programs/src/bin/async_round_trip_synchronous.rs create mode 100644 crates/test-programs/src/bin/async_round_trip_wait.rs create mode 100644 crates/test-programs/src/bin/async_transmit_callee.rs create mode 100644 crates/test-programs/src/bin/async_transmit_caller.rs create mode 100644 crates/test-programs/src/bin/async_yield_callee.rs create mode 100644 crates/test-programs/src/bin/async_yield_caller.rs create mode 100644 crates/wasmtime/src/runtime/component/concurrent.rs create mode 100644 crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs create mode 100644 crates/wasmtime/src/runtime/component/concurrent/ready_chunks.rs create mode 100644 crates/wasmtime/src/runtime/component/concurrent/table.rs create mode 100644 crates/wasmtime/src/runtime/vm/component/states.rs diff --git a/Cargo.lock b/Cargo.lock index 367a482a9741..47e4e416fe3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -258,6 +258,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -610,6 +619,24 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "component-async-tests" +version = "0.0.0" +dependencies = [ + "anyhow", + "flate2", + "futures", + "pretty_env_logger", + "tempfile", + "test-programs-artifacts", + "tokio", + "wasi-http-draft", + "wasm-compose", + "wasmparser", + "wasmtime", + "wasmtime-wasi", +] + [[package]] name = "component-fuzz-util" version = "0.0.0" @@ -1345,6 +1372,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flagset" version = "0.4.3" @@ -1405,12 +1438,13 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.27" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1419,9 +1453,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1433,11 +1467,33 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] [[package]] name = "futures-sink" @@ -1457,11 +1513,16 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1740,6 +1801,20 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "im-rc" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -2378,6 +2453,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2560,6 +2645,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -2879,6 +2973,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2952,6 +3059,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "slab" version = "0.4.7" @@ -3191,14 +3308,17 @@ version = "0.0.0" dependencies = [ "anyhow", "base64 0.21.0", + "flate2", "futures", "getrandom", "libc", + "once_cell", "sha2", "url", "wasi", "wasi-nn", "wit-bindgen", + "wit-bindgen-rt", ] [[package]] @@ -3207,8 +3327,9 @@ version = "0.0.0" dependencies = [ "cargo_metadata", "heck 0.5.0", + "wasmparser", "wasmtime", - "wit-component 0.221.2", + "wit-component", ] [[package]] @@ -3523,6 +3644,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -3617,7 +3744,7 @@ name = "verify-component-adapter" version = "29.0.0" dependencies = [ "anyhow", - "wasmparser 0.221.2", + "wasmparser", "wat", ] @@ -3692,6 +3819,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "wasi-http-draft" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "wasmtime", +] + [[package]] name = "wasi-nn" version = "0.6.0" @@ -3709,7 +3845,7 @@ dependencies = [ "byte-array-literals", "object", "wasi", - "wasm-encoder 0.221.2", + "wasm-encoder", "wit-bindgen-rust-macro", ] @@ -3769,46 +3905,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] -name = "wasm-encoder" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" +name = "wasm-compose" +version = "0.221.2" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ - "leb128", - "wasmparser 0.220.0", + "anyhow", + "heck 0.4.1", + "im-rc", + "indexmap 2.2.6", + "log", + "petgraph", + "serde", + "serde_derive", + "serde_yaml", + "smallvec", + "wasm-encoder", + "wasmparser", + "wat", ] [[package]] name = "wasm-encoder" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "leb128", - "wasmparser 0.221.2", -] - -[[package]] -name = "wasm-metadata" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3e5f5920c5abfc45573c89b07b38efdaae1515ef86f83dad12d60e50ecd62b" -dependencies = [ - "anyhow", - "indexmap 2.2.6", - "serde", - "serde_derive", - "serde_json", - "spdx", - "wasm-encoder 0.220.0", - "wasmparser 0.220.0", + "wasmparser", ] [[package]] name = "wasm-metadata" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7018a96c4f55a8f339954c66e09728f2d6112689000e58f15f6a6d7436e8f" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "anyhow", "indexmap 2.2.6", @@ -3816,36 +3944,34 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.221.2", - "wasmparser 0.221.2", + "wasm-encoder", + "wasmparser", ] [[package]] name = "wasm-mutate" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2512b64553d7c800b798e8e0d0e2e209e76f9330f66ed59991893590ae03cfa3" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "egg", "log", "rand", "thiserror", - "wasm-encoder 0.221.2", - "wasmparser 0.221.2", + "wasm-encoder", + "wasmparser", ] [[package]] name = "wasm-smith" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbaf9c781fb7091b79ad3baa40862b3afccb2949575bb73bdd77643ed40338c" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "anyhow", "arbitrary", "flagset", "indexmap 2.2.6", "leb128", - "wasm-encoder 0.221.2", + "wasm-encoder", ] [[package]] @@ -3859,13 +3985,12 @@ dependencies = [ [[package]] name = "wasm-wave" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bf15c65cc690791565c9f983bad120330e37f55ce2473161f2c0aaa534c7da" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "indexmap 2.2.6", "logos", "thiserror", - "wit-parser 0.221.2", + "wit-parser", ] [[package]] @@ -3912,24 +4037,10 @@ dependencies = [ "wasmi_core", ] -[[package]] -name = "wasmparser" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" -dependencies = [ - "ahash", - "bitflags 2.6.0", - "hashbrown 0.14.3", - "indexmap 2.2.6", - "semver", -] - [[package]] name = "wasmparser" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -3950,12 +4061,11 @@ dependencies = [ [[package]] name = "wasmprinter" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.221.2", + "wasmparser", ] [[package]] @@ -3971,6 +4081,7 @@ dependencies = [ "cfg-if", "encoding_rs", "env_logger 0.11.5", + "futures", "fxprof-processed-profile", "gimli", "hashbrown 0.14.3", @@ -4000,9 +4111,9 @@ dependencies = [ "tempfile", "trait-variant", "wasi-common", - "wasm-encoder 0.221.2", + "wasm-encoder", "wasm-wave", - "wasmparser 0.221.2", + "wasmparser", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -4145,8 +4256,8 @@ dependencies = [ "trait-variant", "walkdir", "wasi-common", - "wasm-encoder 0.221.2", - "wasmparser 0.221.2", + "wasm-encoder", + "wasmparser", "wasmtime", "wasmtime-cache", "wasmtime-cli-flags", @@ -4166,7 +4277,7 @@ dependencies = [ "wast 221.0.2", "wat", "windows-sys 0.59.0", - "wit-component 0.221.2", + "wit-component", ] [[package]] @@ -4199,7 +4310,7 @@ dependencies = [ "wasmtime", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.221.2", + "wit-parser", ] [[package]] @@ -4225,7 +4336,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror", - "wasmparser 0.221.2", + "wasmparser", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -4251,8 +4362,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.221.2", - "wasmparser 0.221.2", + "wasm-encoder", + "wasmparser", "wasmprinter", "wasmtime-component-util", "wat", @@ -4266,7 +4377,7 @@ dependencies = [ "component-fuzz-util", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.221.2", + "wasmparser", "wasmprinter", "wasmtime-environ", "wat", @@ -4325,7 +4436,7 @@ dependencies = [ "rand", "smallvec", "target-lexicon", - "wasmparser 0.221.2", + "wasmparser", "wasmtime", "wasmtime-fuzzing", ] @@ -4346,12 +4457,12 @@ dependencies = [ "target-lexicon", "tempfile", "v8", - "wasm-encoder 0.221.2", + "wasm-encoder", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.221.2", + "wasmparser", "wasmprinter", "wasmtime", "wasmtime-wast", @@ -4552,7 +4663,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.221.2", + "wasmparser", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -4565,7 +4676,7 @@ dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.2.6", - "wit-parser 0.221.2", + "wit-parser", ] [[package]] @@ -4584,21 +4695,19 @@ dependencies = [ [[package]] name = "wast" version = "221.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc4470b9de917ba199157d1f0ae104f2ae362be728c43e68c571c7715bd629e" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.221.2", + "wasm-encoder", ] [[package]] name = "wat" version = "1.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1f3c6d82af47286494c6caea1d332037f5cbeeac82bbf5ef59cb8c201c466e" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "wast 221.0.2", ] @@ -4741,7 +4850,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.221.2", + "wasmparser", "wasmtime-cranelift", "wasmtime-environ", ] @@ -4964,9 +5073,8 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c966692b6d8c4bb3c1aee3313e0930f44457a5763cfffb666f814506124e4691" +version = "0.36.0" +source = "git+https://github.com/dicej/wit-bindgen?branch=async-v1.221.2#ea17e8e99ae247373f8de3e134d9d184dea8957d" dependencies = [ "wit-bindgen-rt", "wit-bindgen-rust-macro", @@ -4974,45 +5082,43 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19857cff2a480fece56ea43f9199322ee5014688a3539ebf8d29ae62d75a3a1f" +version = "0.36.0" +source = "git+https://github.com/dicej/wit-bindgen?branch=async-v1.221.2#ea17e8e99ae247373f8de3e134d9d184dea8957d" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.220.0", + "wit-parser", ] [[package]] name = "wit-bindgen-rt" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605d5562e971a823cf5a550a9d69cb7144e9b4969a810043127175517a4e1865" +version = "0.36.0" +source = "git+https://github.com/dicej/wit-bindgen?branch=async-v1.221.2#ea17e8e99ae247373f8de3e134d9d184dea8957d" dependencies = [ "bitflags 2.6.0", + "futures", + "once_cell", ] [[package]] name = "wit-bindgen-rust" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d894e599c161d71acb6a78e8ec8a609a09c2bb227de50829f5afbd03b844a69" +version = "0.36.0" +source = "git+https://github.com/dicej/wit-bindgen?branch=async-v1.221.2#ea17e8e99ae247373f8de3e134d9d184dea8957d" dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.2.6", "prettyplease", "syn 2.0.90", - "wasm-metadata 0.220.0", + "wasm-metadata", "wit-bindgen-core", - "wit-component 0.220.0", + "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a723cf943bba3bf34f437eb101e5a78180971e25dfb0085d80dbe4e73afb2854" +version = "0.36.0" +source = "git+https://github.com/dicej/wit-bindgen?branch=async-v1.221.2#ea17e8e99ae247373f8de3e134d9d184dea8957d" dependencies = [ "anyhow", "prettyplease", @@ -5023,30 +5129,10 @@ dependencies = [ "wit-bindgen-rust", ] -[[package]] -name = "wit-component" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ccedf54cc65f287da268d64d2bf4f7530d2cfb2296ffbe3ad5f65567e4cf53" -dependencies = [ - "anyhow", - "bitflags 2.6.0", - "indexmap 2.2.6", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder 0.220.0", - "wasm-metadata 0.220.0", - "wasmparser 0.220.0", - "wit-parser 0.220.0", -] - [[package]] name = "wit-component" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6b907a1af1f2cf2160d7fe2ff5967cef120dc5c034d22593a1f24e40272cb2" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -5055,35 +5141,16 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.221.2", - "wasm-metadata 0.221.2", - "wasmparser 0.221.2", - "wit-parser 0.221.2", -] - -[[package]] -name = "wit-parser" -version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7117ce3adc0b4354b46dc1cf3190b00b333e65243d244c613ffcc58bdec84d" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.2.6", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.220.0", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", ] [[package]] name = "wit-parser" version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" +source = "git+https://github.com/dicej/wasm-tools?branch=async-v1.221.2#c019adc93d996839569169e7dfe53425b3629474" dependencies = [ "anyhow", "id-arena", @@ -5094,7 +5161,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.221.2", + "wasmparser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 588914a99b9d..970dcdeca852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -146,6 +146,7 @@ members = [ "crates/bench-api", "crates/c-api/artifact", "crates/environ/fuzz", + "crates/misc/component-async-tests", "crates/test-programs", "crates/wasi-preview1-component-adapter", "crates/wasi-preview1-component-adapter/verify", @@ -229,6 +230,7 @@ wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", ve wasmtime-slab = { path = "crates/slab", version = "=29.0.0" } component-test-util = { path = "crates/misc/component-test-util" } component-fuzz-util = { path = "crates/misc/component-fuzz-util" } +component-async-tests = { path = "crates/misc/component-async-tests" } wiggle = { path = "crates/wiggle", version = "=29.0.0", default-features = false } wiggle-macro = { path = "crates/wiggle/macro", version = "=29.0.0" } wiggle-generate = { path = "crates/wiggle/generate", version = "=29.0.0" } @@ -281,20 +283,22 @@ io-lifetimes = { version = "2.0.3", default-features = false } io-extras = "0.18.1" rustix = "0.38.31" # wit-bindgen: -wit-bindgen = { version = "0.35.0", default-features = false } -wit-bindgen-rust-macro = { version = "0.35.0", default-features = false } +wit-bindgen = { git = "https://github.com/dicej/wit-bindgen", branch = "async-v1.221.2", default-features = false } +wit-bindgen-rt = { git = "https://github.com/dicej/wit-bindgen", branch = "async-v1.221.2", default-features = false } +wit-bindgen-rust-macro = { git = "https://github.com/dicej/wit-bindgen", branch = "async-v1.221.2", default-features = false } # wasm-tools family: -wasmparser = { version = "0.221.2", default-features = false, features = ['simd'] } -wat = "1.221.2" -wast = "221.0.2" -wasmprinter = "0.221.2" -wasm-encoder = "0.221.2" -wasm-smith = "0.221.2" -wasm-mutate = "0.221.2" -wit-parser = "0.221.2" -wit-component = "0.221.2" -wasm-wave = "0.221.2" +wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2", default-features = false, features = ['simd'] } +wat = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wast = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasmprinter = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasm-smith = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasm-mutate = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasm-wave = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } +wasm-compose = { git = "https://github.com/dicej/wasm-tools", branch = "async-v1.221.2" } # Non-Bytecode Alliance maintained dependencies: # -------------------------- diff --git a/benches/call.rs b/benches/call.rs index 8e7d95aa8ffb..4645d97fa38b 100644 --- a/benches/call.rs +++ b/benches/call.rs @@ -628,7 +628,8 @@ mod component { + PartialEq + Debug + Send - + Sync, + + Sync + + 'static, { // Benchmark the "typed" version. c.bench_function(&format!("component - host-to-wasm - typed - {name}"), |b| { diff --git a/crates/component-macro/Cargo.toml b/crates/component-macro/Cargo.toml index 79dbc6a27353..5e38dd2977ce 100644 --- a/crates/component-macro/Cargo.toml +++ b/crates/component-macro/Cargo.toml @@ -41,3 +41,4 @@ similar = { workspace = true } [features] async = [] std = ['wasmtime-wit-bindgen/std'] +component-model-async = ['std', 'async', 'wasmtime-wit-bindgen/component-model-async'] diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index b33bbc5bcb7c..10b2c415bc15 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -1,14 +1,15 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; -use std::collections::HashMap; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{braced, token, Token}; -use wasmtime_wit_bindgen::{AsyncConfig, Opts, Ownership, TrappableError, TrappableImports}; +use wasmtime_wit_bindgen::{ + AsyncConfig, CallStyle, Opts, Ownership, TrappableError, TrappableImports, +}; use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; pub struct Config { @@ -20,13 +21,22 @@ pub struct Config { } pub fn expand(input: &Config) -> Result { - if !cfg!(feature = "async") && input.opts.async_.maybe_async() { + 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())), @@ -40,7 +50,10 @@ pub fn expand(input: &Config) -> Result { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if input.include_generated_code_from_file || std::env::var("WASMTIME_DEBUG_BINDGEN").is_ok() { + if input.include_generated_code_from_file + || input.opts.debug + || std::env::var("WASMTIME_DEBUG_BINDGEN").is_ok() + { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &input.resolve.worlds[input.world].name; @@ -107,6 +120,7 @@ impl Parse for Config { } 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")); @@ -114,6 +128,8 @@ impl Parse for 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, @@ -138,7 +154,7 @@ impl Parse for Config { "cannot specify a world with `interfaces`", )); } - world = Some("interfaces".to_string()); + world = Some("wasmtime:component-macro-synthesized/interfaces".to_string()); opts.only_interfaces = true; } @@ -281,6 +297,9 @@ mod kw { 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); } enum Opt { @@ -301,12 +320,19 @@ enum Opt { RequireStoreDataSend(bool), WasmtimeCrate(syn::Path), IncludeGeneratedCodeFromFile(bool), + ConcurrentImports(bool), + ConcurrentExports(bool), + Debug(bool), } impl Parse for Opt { fn parse(input: ParseStream<'_>) -> Result { let l = input.lookahead1(); - if l.peek(kw::path) { + if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse::()?.value)) + } else if l.peek(kw::path) { input.parse::()?; input.parse::()?; @@ -380,6 +406,14 @@ impl Parse for Opt { 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::()?; diff --git a/crates/component-macro/tests/expanded/char.rs b/crates/component-macro/tests/expanded/char.rs index c8e1b9436487..f1424dc05964 100644 --- a/crates/component-macro/tests/expanded/char.rs +++ b/crates/component-macro/tests/expanded/char.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -194,19 +197,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/chars")?; inst.func_wrap( @@ -354,7 +361,10 @@ pub mod exports { &self, mut store: S, arg0: char, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (char,), @@ -369,7 +379,10 @@ pub mod exports { pub fn call_return_char( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/char_async.rs b/crates/component-macro/tests/expanded/char_async.rs index dcd1653efacf..41a1ff6a8aec 100644 --- a/crates/component-macro/tests/expanded/char_async.rs +++ b/crates/component-macro/tests/expanded/char_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -202,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -372,7 +373,7 @@ pub mod exports { arg0: char, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -392,7 +393,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/char_tracing_async.rs b/crates/component-macro/tests/expanded/char_tracing_async.rs index aace81d094b6..cb39b8ffbcae 100644 --- a/crates/component-macro/tests/expanded/char_tracing_async.rs +++ b/crates/component-macro/tests/expanded/char_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -202,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -401,7 +402,7 @@ pub mod exports { arg0: char, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -430,7 +431,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/conventions.rs b/crates/component-macro/tests/expanded/conventions.rs index 631be530d8b8..817c81b47643 100644 --- a/crates/component-macro/tests/expanded/conventions.rs +++ b/crates/component-macro/tests/expanded/conventions.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -242,19 +245,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/conventions")?; inst.func_wrap( @@ -646,7 +653,10 @@ pub mod exports { pub fn call_kebab_case( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -661,7 +671,10 @@ pub mod exports { &self, mut store: S, arg0: LudicrousSpeed, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (LudicrousSpeed,), @@ -675,7 +688,10 @@ pub mod exports { pub fn call_function_with_dashes( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -688,7 +704,10 @@ pub mod exports { } pub fn call_function_with_no_weird_characters< S: wasmtime::AsContextMut, - >(&self, mut store: S) -> wasmtime::Result<()> { + >(&self, mut store: S) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -702,7 +721,10 @@ pub mod exports { pub fn call_apple( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -716,7 +738,10 @@ pub mod exports { pub fn call_apple_pear( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -730,7 +755,10 @@ pub mod exports { pub fn call_apple_pear_grape( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -744,7 +772,10 @@ pub mod exports { pub fn call_a0( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -763,7 +794,10 @@ pub mod exports { pub fn call_is_xml( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -777,7 +811,10 @@ pub mod exports { pub fn call_explicit( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -791,7 +828,10 @@ pub mod exports { pub fn call_explicit_kebab( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -806,7 +846,10 @@ pub mod exports { pub fn call_bool( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/conventions_async.rs b/crates/component-macro/tests/expanded/conventions_async.rs index 83ed6298a13e..201445f8ef6c 100644 --- a/crates/component-macro/tests/expanded/conventions_async.rs +++ b/crates/component-macro/tests/expanded/conventions_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -250,19 +247,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -684,7 +685,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -702,7 +703,7 @@ pub mod exports { arg0: LudicrousSpeed, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -721,7 +722,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -737,7 +738,7 @@ pub mod exports { S: wasmtime::AsContextMut, >(&self, mut store: S) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -754,7 +755,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -771,7 +772,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -788,7 +789,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -805,7 +806,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -827,7 +828,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -844,7 +845,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -861,7 +862,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -879,7 +880,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/conventions_tracing_async.rs b/crates/component-macro/tests/expanded/conventions_tracing_async.rs index 3fb77150623f..523ed6c3069d 100644 --- a/crates/component-macro/tests/expanded/conventions_tracing_async.rs +++ b/crates/component-macro/tests/expanded/conventions_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -250,19 +247,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -844,7 +845,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -873,7 +874,7 @@ pub mod exports { arg0: LudicrousSpeed, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -901,7 +902,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -928,7 +929,7 @@ pub mod exports { S: wasmtime::AsContextMut, >(&self, mut store: S) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -957,7 +958,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -985,7 +986,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1013,7 +1014,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1041,7 +1042,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1074,7 +1075,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1102,7 +1103,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1130,7 +1131,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1159,7 +1160,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/dead-code.rs b/crates/component-macro/tests/expanded/dead-code.rs index 30781af56403..7a7f24bb9186 100644 --- a/crates/component-macro/tests/expanded/dead-code.rs +++ b/crates/component-macro/tests/expanded/dead-code.rs @@ -18,7 +18,7 @@ impl Clone for ImportsPre { } } } -impl<_T> ImportsPre<_T> { +impl<_T: 'static> ImportsPre<_T> { /// Creates a new copy of `ImportsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; ImportsPre::new(pre)?.instantiate(store) } @@ -200,19 +203,23 @@ pub mod a { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("a:b/interface-with-live-type")?; inst.func_wrap( @@ -247,19 +254,23 @@ pub mod a { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("a:b/interface-with-dead-type")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/dead-code_async.rs b/crates/component-macro/tests/expanded/dead-code_async.rs index 59a4a1a91fcf..7e651f91c4a4 100644 --- a/crates/component-macro/tests/expanded/dead-code_async.rs +++ b/crates/component-macro/tests/expanded/dead-code_async.rs @@ -18,7 +18,7 @@ impl Clone for ImportsPre { } } } -impl<_T> ImportsPre<_T> { +impl<_T: Send + 'static> ImportsPre<_T> { /// Creates a new copy of `ImportsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> ImportsPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; ImportsPre::new(pre)?.instantiate_async(store).await @@ -208,19 +205,23 @@ pub mod a { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -262,19 +263,23 @@ pub mod a { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 5754b8c1371c..20ee2661891f 100644 --- a/crates/component-macro/tests/expanded/dead-code_tracing_async.rs +++ b/crates/component-macro/tests/expanded/dead-code_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for ImportsPre { } } } -impl<_T> ImportsPre<_T> { +impl<_T: Send + 'static> ImportsPre<_T> { /// Creates a new copy of `ImportsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> ImportsPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; ImportsPre::new(pre)?.instantiate_async(store).await @@ -208,19 +205,23 @@ pub mod a { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -275,19 +276,23 @@ pub mod a { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/direct-import.rs b/crates/component-macro/tests/expanded/direct-import.rs index 225fe6c9009f..eb1249d8bffb 100644 --- a/crates/component-macro/tests/expanded/direct-import.rs +++ b/crates/component-macro/tests/expanded/direct-import.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -162,7 +162,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate(store) } diff --git a/crates/component-macro/tests/expanded/direct-import_async.rs b/crates/component-macro/tests/expanded/direct-import_async.rs index a0ab29ebc481..2558ecafb028 100644 --- a/crates/component-macro/tests/expanded/direct-import_async.rs +++ b/crates/component-macro/tests/expanded/direct-import_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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) @@ -168,7 +165,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await 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 1ee124c08a94..05089c3dcc81 100644 --- a/crates/component-macro/tests/expanded/direct-import_tracing_async.rs +++ b/crates/component-macro/tests/expanded/direct-import_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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) @@ -168,7 +165,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await diff --git a/crates/component-macro/tests/expanded/empty.rs b/crates/component-macro/tests/expanded/empty.rs index 573b02c84e74..fb6e5d5e60ec 100644 --- a/crates/component-macro/tests/expanded/empty.rs +++ b/crates/component-macro/tests/expanded/empty.rs @@ -18,7 +18,7 @@ impl Clone for EmptyPre { } } } -impl<_T> EmptyPre<_T> { +impl<_T: 'static> EmptyPre<_T> { /// Creates a new copy of `EmptyPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; EmptyPre::new(pre)?.instantiate(store) } diff --git a/crates/component-macro/tests/expanded/empty_async.rs b/crates/component-macro/tests/expanded/empty_async.rs index d2eea87b72a0..264f37601052 100644 --- a/crates/component-macro/tests/expanded/empty_async.rs +++ b/crates/component-macro/tests/expanded/empty_async.rs @@ -18,7 +18,7 @@ impl Clone for EmptyPre { } } } -impl<_T> EmptyPre<_T> { +impl<_T: Send + 'static> EmptyPre<_T> { /// Creates a new copy of `EmptyPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> EmptyPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { 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 d2eea87b72a0..264f37601052 100644 --- a/crates/component-macro/tests/expanded/empty_tracing_async.rs +++ b/crates/component-macro/tests/expanded/empty_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for EmptyPre { } } } -impl<_T> EmptyPre<_T> { +impl<_T: Send + 'static> EmptyPre<_T> { /// Creates a new copy of `EmptyPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> EmptyPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { 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 c036a1a2d85b..931a6c8edd1e 100644 --- a/crates/component-macro/tests/expanded/flags.rs +++ b/crates/component-macro/tests/expanded/flags.rs @@ -18,7 +18,7 @@ impl Clone for TheFlagsPre { } } } -impl<_T> TheFlagsPre<_T> { +impl<_T: 'static> TheFlagsPre<_T> { /// Creates a new copy of `TheFlagsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheFlagsPre::new(pre)?.instantiate(store) } @@ -311,19 +314,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/flegs")?; inst.func_wrap( @@ -751,7 +758,10 @@ pub mod exports { &self, mut store: S, arg0: Flag1, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag1,), @@ -766,7 +776,10 @@ pub mod exports { &self, mut store: S, arg0: Flag2, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag2,), @@ -781,7 +794,10 @@ pub mod exports { &self, mut store: S, arg0: Flag4, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag4,), @@ -796,7 +812,10 @@ pub mod exports { &self, mut store: S, arg0: Flag8, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag8,), @@ -811,7 +830,10 @@ pub mod exports { &self, mut store: S, arg0: Flag16, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag16,), @@ -826,7 +848,10 @@ pub mod exports { &self, mut store: S, arg0: Flag32, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag32,), @@ -841,7 +866,10 @@ pub mod exports { &self, mut store: S, arg0: Flag64, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Flag64,), diff --git a/crates/component-macro/tests/expanded/flags_async.rs b/crates/component-macro/tests/expanded/flags_async.rs index b29ad900f969..49fec56c1cd7 100644 --- a/crates/component-macro/tests/expanded/flags_async.rs +++ b/crates/component-macro/tests/expanded/flags_async.rs @@ -18,7 +18,7 @@ impl Clone for TheFlagsPre { } } } -impl<_T> TheFlagsPre<_T> { +impl<_T: Send + 'static> TheFlagsPre<_T> { /// Creates a new copy of `TheFlagsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheFlagsPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheFlagsPre::new(pre)?.instantiate_async(store).await @@ -319,19 +316,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -779,7 +780,7 @@ pub mod exports { arg0: Flag1, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -799,7 +800,7 @@ pub mod exports { arg0: Flag2, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -819,7 +820,7 @@ pub mod exports { arg0: Flag4, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -839,7 +840,7 @@ pub mod exports { arg0: Flag8, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -859,7 +860,7 @@ pub mod exports { arg0: Flag16, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -879,7 +880,7 @@ pub mod exports { arg0: Flag32, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -899,7 +900,7 @@ pub mod exports { arg0: Flag64, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/flags_tracing_async.rs b/crates/component-macro/tests/expanded/flags_tracing_async.rs index 30fb04df8e5c..bf8d98dd8260 100644 --- a/crates/component-macro/tests/expanded/flags_tracing_async.rs +++ b/crates/component-macro/tests/expanded/flags_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheFlagsPre { } } } -impl<_T> TheFlagsPre<_T> { +impl<_T: Send + 'static> TheFlagsPre<_T> { /// Creates a new copy of `TheFlagsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheFlagsPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheFlagsPre::new(pre)?.instantiate_async(store).await @@ -319,19 +316,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -891,7 +892,7 @@ pub mod exports { arg0: Flag1, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -920,7 +921,7 @@ pub mod exports { arg0: Flag2, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -949,7 +950,7 @@ pub mod exports { arg0: Flag4, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -978,7 +979,7 @@ pub mod exports { arg0: Flag8, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1007,7 +1008,7 @@ pub mod exports { arg0: Flag16, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1036,7 +1037,7 @@ pub mod exports { arg0: Flag32, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1065,7 +1066,7 @@ pub mod exports { arg0: Flag64, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/floats.rs b/crates/component-macro/tests/expanded/floats.rs index 50f7b3ca973f..1a8d0aa3bb9f 100644 --- a/crates/component-macro/tests/expanded/floats.rs +++ b/crates/component-macro/tests/expanded/floats.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -194,19 +197,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/floats")?; inst.func_wrap( @@ -386,7 +393,10 @@ pub mod exports { &self, mut store: S, arg0: f32, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (f32,), @@ -401,7 +411,10 @@ pub mod exports { &self, mut store: S, arg0: f64, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (f64,), @@ -415,7 +428,10 @@ pub mod exports { pub fn call_f32_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -429,7 +445,10 @@ pub mod exports { pub fn call_f64_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/floats_async.rs b/crates/component-macro/tests/expanded/floats_async.rs index a489394754c4..46ea8349b721 100644 --- a/crates/component-macro/tests/expanded/floats_async.rs +++ b/crates/component-macro/tests/expanded/floats_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -202,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -408,7 +409,7 @@ pub mod exports { arg0: f32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -428,7 +429,7 @@ pub mod exports { arg0: f64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -447,7 +448,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -466,7 +467,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/floats_tracing_async.rs b/crates/component-macro/tests/expanded/floats_tracing_async.rs index f711cafffa7a..6d22d5f13b82 100644 --- a/crates/component-macro/tests/expanded/floats_tracing_async.rs +++ b/crates/component-macro/tests/expanded/floats_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -202,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -466,7 +467,7 @@ pub mod exports { arg0: f32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -495,7 +496,7 @@ pub mod exports { arg0: f64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -523,7 +524,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -551,7 +552,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/function-new.rs b/crates/component-macro/tests/expanded/function-new.rs index ca37b6668473..45c584d31fb0 100644 --- a/crates/component-macro/tests/expanded/function-new.rs +++ b/crates/component-macro/tests/expanded/function-new.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate(store) } @@ -170,7 +173,10 @@ const _: () = { pub fn call_new( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.new) }; diff --git a/crates/component-macro/tests/expanded/function-new_async.rs b/crates/component-macro/tests/expanded/function-new_async.rs index 38a06794f9a6..84bad64ba974 100644 --- a/crates/component-macro/tests/expanded/function-new_async.rs +++ b/crates/component-macro/tests/expanded/function-new_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -178,7 +175,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.new) 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 da3f9d8c9596..c20a45c27d6a 100644 --- a/crates/component-macro/tests/expanded/function-new_tracing_async.rs +++ b/crates/component-macro/tests/expanded/function-new_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -178,7 +175,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/host-world.rs b/crates/component-macro/tests/expanded/host-world.rs index b5b514902114..40b4b6a1258c 100644 --- a/crates/component-macro/tests/expanded/host-world.rs +++ b/crates/component-macro/tests/expanded/host-world.rs @@ -18,7 +18,7 @@ impl Clone for Host_Pre { } } } -impl<_T> Host_Pre<_T> { +impl<_T: 'static> Host_Pre<_T> { /// Creates a new copy of `Host_Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -162,7 +162,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; Host_Pre::new(pre)?.instantiate(store) } diff --git a/crates/component-macro/tests/expanded/host-world_async.rs b/crates/component-macro/tests/expanded/host-world_async.rs index d6dee9406e8c..ce1c509a9b62 100644 --- a/crates/component-macro/tests/expanded/host-world_async.rs +++ b/crates/component-macro/tests/expanded/host-world_async.rs @@ -18,7 +18,7 @@ impl Clone for Host_Pre { } } } -impl<_T> Host_Pre<_T> { +impl<_T: Send + 'static> Host_Pre<_T> { /// Creates a new copy of `Host_Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Host_Pre<_T> { 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) @@ -168,7 +165,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Host_Pre::new(pre)?.instantiate_async(store).await 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 8ef92dbe2326..f86a9671d72a 100644 --- a/crates/component-macro/tests/expanded/host-world_tracing_async.rs +++ b/crates/component-macro/tests/expanded/host-world_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for Host_Pre { } } } -impl<_T> Host_Pre<_T> { +impl<_T: Send + 'static> Host_Pre<_T> { /// Creates a new copy of `Host_Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Host_Pre<_T> { 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) @@ -168,7 +165,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Host_Pre::new(pre)?.instantiate_async(store).await diff --git a/crates/component-macro/tests/expanded/integers.rs b/crates/component-macro/tests/expanded/integers.rs index a0ea4d42e45e..181cf562051b 100644 --- a/crates/component-macro/tests/expanded/integers.rs +++ b/crates/component-macro/tests/expanded/integers.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -218,19 +221,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/integers")?; inst.func_wrap( @@ -714,7 +721,10 @@ pub mod exports { &self, mut store: S, arg0: u8, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u8,), @@ -729,7 +739,10 @@ pub mod exports { &self, mut store: S, arg0: i8, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (i8,), @@ -744,7 +757,10 @@ pub mod exports { &self, mut store: S, arg0: u16, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u16,), @@ -759,7 +775,10 @@ pub mod exports { &self, mut store: S, arg0: i16, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (i16,), @@ -774,7 +793,10 @@ pub mod exports { &self, mut store: S, arg0: u32, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u32,), @@ -789,7 +811,10 @@ pub mod exports { &self, mut store: S, arg0: i32, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (i32,), @@ -804,7 +829,10 @@ pub mod exports { &self, mut store: S, arg0: u64, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u64,), @@ -819,7 +847,10 @@ pub mod exports { &self, mut store: S, arg0: i64, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (i64,), @@ -841,7 +872,10 @@ pub mod exports { arg5: i32, arg6: u64, arg7: i64, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u8, i8, u16, i16, u32, i32, u64, i64), @@ -859,7 +893,10 @@ pub mod exports { pub fn call_r1( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -873,7 +910,10 @@ pub mod exports { pub fn call_r2( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -887,7 +927,10 @@ pub mod exports { pub fn call_r3( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -901,7 +944,10 @@ pub mod exports { pub fn call_r4( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -915,7 +961,10 @@ pub mod exports { pub fn call_r5( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -929,7 +978,10 @@ pub mod exports { pub fn call_r6( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -943,7 +995,10 @@ pub mod exports { pub fn call_r7( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -957,7 +1012,10 @@ pub mod exports { pub fn call_r8( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -971,7 +1029,10 @@ pub mod exports { pub fn call_pair_ret( &self, mut store: S, - ) -> wasmtime::Result<(i64, u8)> { + ) -> wasmtime::Result<(i64, u8)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/integers_async.rs b/crates/component-macro/tests/expanded/integers_async.rs index 414c50dff3db..2b8e9ecd0841 100644 --- a/crates/component-macro/tests/expanded/integers_async.rs +++ b/crates/component-macro/tests/expanded/integers_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -226,19 +223,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -765,7 +766,7 @@ pub mod exports { arg0: u8, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -785,7 +786,7 @@ pub mod exports { arg0: i8, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -805,7 +806,7 @@ pub mod exports { arg0: u16, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -825,7 +826,7 @@ pub mod exports { arg0: i16, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -845,7 +846,7 @@ pub mod exports { arg0: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -865,7 +866,7 @@ pub mod exports { arg0: i32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -885,7 +886,7 @@ pub mod exports { arg0: u64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -905,7 +906,7 @@ pub mod exports { arg0: i64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -932,7 +933,7 @@ pub mod exports { arg7: i64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -954,7 +955,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -973,7 +974,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -992,7 +993,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1011,7 +1012,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1030,7 +1031,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1049,7 +1050,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1068,7 +1069,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1087,7 +1088,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1106,7 +1107,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(i64, u8)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/integers_tracing_async.rs b/crates/component-macro/tests/expanded/integers_tracing_async.rs index abda88c6fb1c..4b89ace691b0 100644 --- a/crates/component-macro/tests/expanded/integers_tracing_async.rs +++ b/crates/component-macro/tests/expanded/integers_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -226,19 +223,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1030,7 +1031,7 @@ pub mod exports { arg0: u8, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1059,7 +1060,7 @@ pub mod exports { arg0: i8, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1088,7 +1089,7 @@ pub mod exports { arg0: u16, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1117,7 +1118,7 @@ pub mod exports { arg0: i16, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1146,7 +1147,7 @@ pub mod exports { arg0: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1175,7 +1176,7 @@ pub mod exports { arg0: i32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1204,7 +1205,7 @@ pub mod exports { arg0: u64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1233,7 +1234,7 @@ pub mod exports { arg0: i64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1269,7 +1270,7 @@ pub mod exports { arg7: i64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1300,7 +1301,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1328,7 +1329,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1356,7 +1357,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1384,7 +1385,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1412,7 +1413,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1440,7 +1441,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1468,7 +1469,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1496,7 +1497,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1524,7 +1525,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(i64, u8)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/lists.rs b/crates/component-macro/tests/expanded/lists.rs index d5067c796dc5..db311d6f16bb 100644 --- a/crates/component-macro/tests/expanded/lists.rs +++ b/crates/component-macro/tests/expanded/lists.rs @@ -18,7 +18,7 @@ impl Clone for TheListsPre { } } } -impl<_T> TheListsPre<_T> { +impl<_T: 'static> TheListsPre<_T> { /// Creates a new copy of `TheListsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheListsPre::new(pre)?.instantiate(store) } @@ -467,19 +470,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/lists")?; inst.func_wrap( @@ -1573,7 +1580,10 @@ pub mod exports { &self, mut store: S, arg0: &[u8], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u8],), @@ -1588,7 +1598,10 @@ pub mod exports { &self, mut store: S, arg0: &[u16], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u16],), @@ -1603,7 +1616,10 @@ pub mod exports { &self, mut store: S, arg0: &[u32], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u32],), @@ -1618,7 +1634,10 @@ pub mod exports { &self, mut store: S, arg0: &[u64], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u64],), @@ -1633,7 +1652,10 @@ pub mod exports { &self, mut store: S, arg0: &[i8], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[i8],), @@ -1648,7 +1670,10 @@ pub mod exports { &self, mut store: S, arg0: &[i16], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[i16],), @@ -1663,7 +1688,10 @@ pub mod exports { &self, mut store: S, arg0: &[i32], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[i32],), @@ -1678,7 +1706,10 @@ pub mod exports { &self, mut store: S, arg0: &[i64], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[i64],), @@ -1693,7 +1724,10 @@ pub mod exports { &self, mut store: S, arg0: &[f32], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[f32],), @@ -1708,7 +1742,10 @@ pub mod exports { &self, mut store: S, arg0: &[f64], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[f64],), @@ -1722,7 +1759,10 @@ pub mod exports { pub fn call_list_u8_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1736,7 +1776,10 @@ pub mod exports { pub fn call_list_u16_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1750,7 +1793,10 @@ pub mod exports { pub fn call_list_u32_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1764,7 +1810,10 @@ pub mod exports { pub fn call_list_u64_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1778,7 +1827,10 @@ pub mod exports { pub fn call_list_s8_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1792,7 +1844,10 @@ pub mod exports { pub fn call_list_s16_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1806,7 +1861,10 @@ pub mod exports { pub fn call_list_s32_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1820,7 +1878,10 @@ pub mod exports { pub fn call_list_s64_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1834,7 +1895,10 @@ pub mod exports { pub fn call_list_f32_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1848,7 +1912,10 @@ pub mod exports { pub fn call_list_f64_ret( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1865,7 +1932,10 @@ pub mod exports { arg0: &[(u8, i8)], ) -> wasmtime::Result< wasmtime::component::__internal::Vec<(i64, u32)>, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[(u8, i8)],), @@ -1880,7 +1950,10 @@ pub mod exports { &self, mut store: S, arg0: &[wasmtime::component::__internal::String], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[wasmtime::component::__internal::String],), @@ -1898,7 +1971,10 @@ pub mod exports { wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1921,7 +1997,10 @@ pub mod exports { wasmtime::component::__internal::Vec< (wasmtime::component::__internal::String, u8), >, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[(u8, wasmtime::component::__internal::String)],), @@ -1944,7 +2023,10 @@ pub mod exports { wasmtime::component::__internal::Vec< wasmtime::component::__internal::String, >, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[wasmtime::component::__internal::String],), @@ -1965,7 +2047,10 @@ pub mod exports { arg0: &[SomeRecord], ) -> wasmtime::Result< wasmtime::component::__internal::Vec, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[SomeRecord],), @@ -1982,7 +2067,10 @@ pub mod exports { arg0: &[OtherRecord], ) -> wasmtime::Result< wasmtime::component::__internal::Vec, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[OtherRecord],), @@ -1999,7 +2087,10 @@ pub mod exports { arg0: &[SomeVariant], ) -> wasmtime::Result< wasmtime::component::__internal::Vec, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[SomeVariant],), @@ -2014,7 +2105,10 @@ pub mod exports { &self, mut store: S, arg0: &LoadStoreAllSizes, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&LoadStoreAllSizes,), diff --git a/crates/component-macro/tests/expanded/lists_async.rs b/crates/component-macro/tests/expanded/lists_async.rs index c9683965cb71..3d6a683a7dc7 100644 --- a/crates/component-macro/tests/expanded/lists_async.rs +++ b/crates/component-macro/tests/expanded/lists_async.rs @@ -18,7 +18,7 @@ impl Clone for TheListsPre { } } } -impl<_T> TheListsPre<_T> { +impl<_T: Send + 'static> TheListsPre<_T> { /// Creates a new copy of `TheListsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheListsPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheListsPre::new(pre)?.instantiate_async(store).await @@ -495,19 +492,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1685,7 +1686,7 @@ pub mod exports { arg0: &[u8], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1705,7 +1706,7 @@ pub mod exports { arg0: &[u16], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1725,7 +1726,7 @@ pub mod exports { arg0: &[u32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1745,7 +1746,7 @@ pub mod exports { arg0: &[u64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1765,7 +1766,7 @@ pub mod exports { arg0: &[i8], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1785,7 +1786,7 @@ pub mod exports { arg0: &[i16], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1805,7 +1806,7 @@ pub mod exports { arg0: &[i32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1825,7 +1826,7 @@ pub mod exports { arg0: &[i64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1845,7 +1846,7 @@ pub mod exports { arg0: &[f32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1865,7 +1866,7 @@ pub mod exports { arg0: &[f64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1884,7 +1885,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1903,7 +1904,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1922,7 +1923,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1941,7 +1942,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1960,7 +1961,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1979,7 +1980,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1998,7 +1999,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2017,7 +2018,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2036,7 +2037,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2055,7 +2056,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2077,7 +2078,7 @@ pub mod exports { wasmtime::component::__internal::Vec<(i64, u32)>, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2097,7 +2098,7 @@ pub mod exports { arg0: &[wasmtime::component::__internal::String], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2120,7 +2121,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2148,7 +2149,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2176,7 +2177,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2202,7 +2203,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2224,7 +2225,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2246,7 +2247,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2266,7 +2267,7 @@ pub mod exports { arg0: &LoadStoreAllSizes, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/lists_tracing_async.rs b/crates/component-macro/tests/expanded/lists_tracing_async.rs index b74add62591d..e980e005f2d5 100644 --- a/crates/component-macro/tests/expanded/lists_tracing_async.rs +++ b/crates/component-macro/tests/expanded/lists_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheListsPre { } } } -impl<_T> TheListsPre<_T> { +impl<_T: Send + 'static> TheListsPre<_T> { /// Creates a new copy of `TheListsPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheListsPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheListsPre::new(pre)?.instantiate_async(store).await @@ -495,19 +492,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -2116,7 +2117,7 @@ pub mod exports { arg0: &[u8], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2145,7 +2146,7 @@ pub mod exports { arg0: &[u16], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2174,7 +2175,7 @@ pub mod exports { arg0: &[u32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2203,7 +2204,7 @@ pub mod exports { arg0: &[u64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2232,7 +2233,7 @@ pub mod exports { arg0: &[i8], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2261,7 +2262,7 @@ pub mod exports { arg0: &[i16], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2290,7 +2291,7 @@ pub mod exports { arg0: &[i32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2319,7 +2320,7 @@ pub mod exports { arg0: &[i64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2348,7 +2349,7 @@ pub mod exports { arg0: &[f32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2377,7 +2378,7 @@ pub mod exports { arg0: &[f64], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2405,7 +2406,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2433,7 +2434,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2461,7 +2462,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2489,7 +2490,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2517,7 +2518,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2545,7 +2546,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2573,7 +2574,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2601,7 +2602,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2629,7 +2630,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2657,7 +2658,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2688,7 +2689,7 @@ pub mod exports { wasmtime::component::__internal::Vec<(i64, u32)>, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2717,7 +2718,7 @@ pub mod exports { arg0: &[wasmtime::component::__internal::String], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2749,7 +2750,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2786,7 +2787,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2823,7 +2824,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2858,7 +2859,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2889,7 +2890,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2920,7 +2921,7 @@ pub mod exports { wasmtime::component::__internal::Vec, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2949,7 +2950,7 @@ pub mod exports { arg0: &LoadStoreAllSizes, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/many-arguments.rs b/crates/component-macro/tests/expanded/many-arguments.rs index 86db694f8323..f9686edb15da 100644 --- a/crates/component-macro/tests/expanded/many-arguments.rs +++ b/crates/component-macro/tests/expanded/many-arguments.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -291,19 +294,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/manyarg")?; inst.func_wrap( @@ -659,7 +666,10 @@ pub mod exports { arg13: u64, arg14: u64, arg15: u64, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< ( @@ -712,7 +722,10 @@ pub mod exports { &self, mut store: S, arg0: &BigStruct, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&BigStruct,), diff --git a/crates/component-macro/tests/expanded/many-arguments_async.rs b/crates/component-macro/tests/expanded/many-arguments_async.rs index 0c5143a8a3a7..053bddc0799d 100644 --- a/crates/component-macro/tests/expanded/many-arguments_async.rs +++ b/crates/component-macro/tests/expanded/many-arguments_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -299,19 +296,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -679,7 +680,7 @@ pub mod exports { arg15: u64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -736,7 +737,7 @@ pub mod exports { arg0: &BigStruct, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 6bfcba0c10bf..7427710fd088 100644 --- a/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs +++ b/crates/component-macro/tests/expanded/many-arguments_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -299,19 +296,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -722,7 +723,7 @@ pub mod exports { arg15: u64, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -788,7 +789,7 @@ pub mod exports { arg0: &BigStruct, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/multi-return.rs b/crates/component-macro/tests/expanded/multi-return.rs index 7b42ddcaeb3c..1e00b26d0174 100644 --- a/crates/component-macro/tests/expanded/multi-return.rs +++ b/crates/component-macro/tests/expanded/multi-return.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -197,19 +200,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/multi-return")?; inst.func_wrap( @@ -401,7 +408,10 @@ pub mod exports { pub fn call_mra( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -415,7 +425,10 @@ pub mod exports { pub fn call_mrb( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -429,7 +442,10 @@ pub mod exports { pub fn call_mrc( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -443,7 +459,10 @@ pub mod exports { pub fn call_mrd( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -457,7 +476,10 @@ pub mod exports { pub fn call_mre( &self, mut store: S, - ) -> wasmtime::Result<(u32, f32)> { + ) -> wasmtime::Result<(u32, f32)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/multi-return_async.rs b/crates/component-macro/tests/expanded/multi-return_async.rs index 4483a732f68d..8169fdfed708 100644 --- a/crates/component-macro/tests/expanded/multi-return_async.rs +++ b/crates/component-macro/tests/expanded/multi-return_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -425,7 +426,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -442,7 +443,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -459,7 +460,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -478,7 +479,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -497,7 +498,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(u32, f32)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/multi-return_tracing_async.rs b/crates/component-macro/tests/expanded/multi-return_tracing_async.rs index 25c65f1be60a..d38f7c433b35 100644 --- a/crates/component-macro/tests/expanded/multi-return_tracing_async.rs +++ b/crates/component-macro/tests/expanded/multi-return_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -490,7 +491,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -518,7 +519,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -546,7 +547,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -574,7 +575,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -602,7 +603,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(u32, f32)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/multiversion.rs b/crates/component-macro/tests/expanded/multiversion.rs index 44d9e429237e..cdc8daf9b46c 100644 --- a/crates/component-macro/tests/expanded/multiversion.rs +++ b/crates/component-macro/tests/expanded/multiversion.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -166,7 +166,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate(store) } @@ -209,19 +212,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("my:dep/a@0.1.0")?; inst.func_wrap( @@ -260,19 +267,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("my:dep/a@0.2.0")?; inst.func_wrap( @@ -390,7 +401,10 @@ pub mod exports { pub fn call_x( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -490,7 +504,10 @@ pub mod exports { pub fn call_x( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/multiversion_async.rs b/crates/component-macro/tests/expanded/multiversion_async.rs index 43fc69677ce3..af5ec740f102 100644 --- a/crates/component-macro/tests/expanded/multiversion_async.rs +++ b/crates/component-macro/tests/expanded/multiversion_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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,7 +168,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -217,19 +214,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -275,19 +276,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -413,7 +418,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -516,7 +521,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/multiversion_tracing_async.rs b/crates/component-macro/tests/expanded/multiversion_tracing_async.rs index c5e6c45376f2..127f0e80d0ed 100644 --- a/crates/component-macro/tests/expanded/multiversion_tracing_async.rs +++ b/crates/component-macro/tests/expanded/multiversion_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> FooPre<_T> { 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,7 +168,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -217,19 +214,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -288,19 +289,23 @@ pub mod my { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -439,7 +444,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -553,7 +558,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/path1.rs b/crates/component-macro/tests/expanded/path1.rs index a4ce53636f65..a338608237fd 100644 --- a/crates/component-macro/tests/expanded/path1.rs +++ b/crates/component-macro/tests/expanded/path1.rs @@ -18,7 +18,7 @@ impl Clone for Path1Pre { } } } -impl<_T> Path1Pre<_T> { +impl<_T: 'static> Path1Pre<_T> { /// Creates a new copy of `Path1Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; Path1Pre::new(pre)?.instantiate(store) } @@ -176,19 +179,23 @@ pub mod paths { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("paths:path1/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path1_async.rs b/crates/component-macro/tests/expanded/path1_async.rs index 80106c574dc1..fcf372084691 100644 --- a/crates/component-macro/tests/expanded/path1_async.rs +++ b/crates/component-macro/tests/expanded/path1_async.rs @@ -18,7 +18,7 @@ impl Clone for Path1Pre { } } } -impl<_T> Path1Pre<_T> { +impl<_T: Send + 'static> Path1Pre<_T> { /// Creates a new copy of `Path1Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Path1Pre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Path1Pre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod paths { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/path1_tracing_async.rs b/crates/component-macro/tests/expanded/path1_tracing_async.rs index 80106c574dc1..fcf372084691 100644 --- a/crates/component-macro/tests/expanded/path1_tracing_async.rs +++ b/crates/component-macro/tests/expanded/path1_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for Path1Pre { } } } -impl<_T> Path1Pre<_T> { +impl<_T: Send + 'static> Path1Pre<_T> { /// Creates a new copy of `Path1Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Path1Pre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Path1Pre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod paths { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/path2.rs b/crates/component-macro/tests/expanded/path2.rs index 5d9fba7b1ba0..7ad7e7351c8e 100644 --- a/crates/component-macro/tests/expanded/path2.rs +++ b/crates/component-macro/tests/expanded/path2.rs @@ -18,7 +18,7 @@ impl Clone for Path2Pre { } } } -impl<_T> Path2Pre<_T> { +impl<_T: 'static> Path2Pre<_T> { /// Creates a new copy of `Path2Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; Path2Pre::new(pre)?.instantiate(store) } @@ -176,19 +179,23 @@ pub mod paths { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("paths:path2/test")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/path2_async.rs b/crates/component-macro/tests/expanded/path2_async.rs index 1eae1817d355..8a4f83ca7a40 100644 --- a/crates/component-macro/tests/expanded/path2_async.rs +++ b/crates/component-macro/tests/expanded/path2_async.rs @@ -18,7 +18,7 @@ impl Clone for Path2Pre { } } } -impl<_T> Path2Pre<_T> { +impl<_T: Send + 'static> Path2Pre<_T> { /// Creates a new copy of `Path2Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Path2Pre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Path2Pre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod paths { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/path2_tracing_async.rs b/crates/component-macro/tests/expanded/path2_tracing_async.rs index 1eae1817d355..8a4f83ca7a40 100644 --- a/crates/component-macro/tests/expanded/path2_tracing_async.rs +++ b/crates/component-macro/tests/expanded/path2_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for Path2Pre { } } } -impl<_T> Path2Pre<_T> { +impl<_T: Send + 'static> Path2Pre<_T> { /// Creates a new copy of `Path2Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> Path2Pre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; Path2Pre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod paths { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/records.rs b/crates/component-macro/tests/expanded/records.rs index 5a6fed8dcbd3..38db67ea33dd 100644 --- a/crates/component-macro/tests/expanded/records.rs +++ b/crates/component-macro/tests/expanded/records.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -347,19 +350,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/records")?; inst.func_wrap( @@ -893,7 +900,10 @@ pub mod exports { &self, mut store: S, arg0: (char, u32), - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< ((char, u32),), @@ -907,7 +917,10 @@ pub mod exports { pub fn call_tuple_result( &self, mut store: S, - ) -> wasmtime::Result<(char, u32)> { + ) -> wasmtime::Result<(char, u32)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -922,7 +935,10 @@ pub mod exports { &self, mut store: S, arg0: Empty, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Empty,), @@ -936,7 +952,10 @@ pub mod exports { pub fn call_empty_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -951,7 +970,10 @@ pub mod exports { &self, mut store: S, arg0: Scalars, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Scalars,), @@ -965,7 +987,10 @@ pub mod exports { pub fn call_scalar_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -980,7 +1005,10 @@ pub mod exports { &self, mut store: S, arg0: ReallyFlags, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (ReallyFlags,), @@ -994,7 +1022,10 @@ pub mod exports { pub fn call_flags_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1009,7 +1040,10 @@ pub mod exports { &self, mut store: S, arg0: &Aggregates, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&Aggregates,), @@ -1023,7 +1057,10 @@ pub mod exports { pub fn call_aggregate_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1038,7 +1075,10 @@ pub mod exports { &self, mut store: S, arg0: TupleTypedef2, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (TupleTypedef2,), diff --git a/crates/component-macro/tests/expanded/records_async.rs b/crates/component-macro/tests/expanded/records_async.rs index 2760af0cf0b4..dc4a0cd311ff 100644 --- a/crates/component-macro/tests/expanded/records_async.rs +++ b/crates/component-macro/tests/expanded/records_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -355,19 +352,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -929,7 +930,7 @@ pub mod exports { arg0: (char, u32), ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -948,7 +949,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(char, u32)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -968,7 +969,7 @@ pub mod exports { arg0: Empty, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -987,7 +988,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1007,7 +1008,7 @@ pub mod exports { arg0: Scalars, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1026,7 +1027,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1046,7 +1047,7 @@ pub mod exports { arg0: ReallyFlags, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1065,7 +1066,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1085,7 +1086,7 @@ pub mod exports { arg0: &Aggregates, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1104,7 +1105,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1124,7 +1125,7 @@ pub mod exports { arg0: TupleTypedef2, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/records_tracing_async.rs b/crates/component-macro/tests/expanded/records_tracing_async.rs index 410c1628fa34..52f00d7f6829 100644 --- a/crates/component-macro/tests/expanded/records_tracing_async.rs +++ b/crates/component-macro/tests/expanded/records_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -355,19 +352,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1090,7 +1091,7 @@ pub mod exports { arg0: (char, u32), ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1118,7 +1119,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(char, u32)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1147,7 +1148,7 @@ pub mod exports { arg0: Empty, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1175,7 +1176,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1204,7 +1205,7 @@ pub mod exports { arg0: Scalars, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1232,7 +1233,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1261,7 +1262,7 @@ pub mod exports { arg0: ReallyFlags, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1289,7 +1290,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1318,7 +1319,7 @@ pub mod exports { arg0: &Aggregates, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1346,7 +1347,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -1375,7 +1376,7 @@ pub mod exports { arg0: TupleTypedef2, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/rename.rs b/crates/component-macro/tests/expanded/rename.rs index 40ad51574259..97ca0e0c822f 100644 --- a/crates/component-macro/tests/expanded/rename.rs +++ b/crates/component-macro/tests/expanded/rename.rs @@ -18,7 +18,7 @@ impl Clone for NeptunePre { } } } -impl<_T> NeptunePre<_T> { +impl<_T: 'static> NeptunePre<_T> { /// Creates a new copy of `NeptunePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; NeptunePre::new(pre)?.instantiate(store) } @@ -182,19 +185,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/green")?; Ok(()) @@ -224,19 +231,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/red")?; inst.func_wrap( diff --git a/crates/component-macro/tests/expanded/rename_async.rs b/crates/component-macro/tests/expanded/rename_async.rs index 72613090032a..5540bb479b42 100644 --- a/crates/component-macro/tests/expanded/rename_async.rs +++ b/crates/component-macro/tests/expanded/rename_async.rs @@ -18,7 +18,7 @@ impl Clone for NeptunePre { } } } -impl<_T> NeptunePre<_T> { +impl<_T: Send + 'static> NeptunePre<_T> { /// Creates a new copy of `NeptunePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> NeptunePre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; NeptunePre::new(pre)?.instantiate_async(store).await @@ -190,19 +187,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -237,19 +238,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/rename_tracing_async.rs b/crates/component-macro/tests/expanded/rename_tracing_async.rs index d44efbea7e8f..89330e85a091 100644 --- a/crates/component-macro/tests/expanded/rename_tracing_async.rs +++ b/crates/component-macro/tests/expanded/rename_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for NeptunePre { } } } -impl<_T> NeptunePre<_T> { +impl<_T: Send + 'static> NeptunePre<_T> { /// Creates a new copy of `NeptunePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> NeptunePre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; NeptunePre::new(pre)?.instantiate_async(store).await @@ -190,19 +187,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -237,19 +238,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/resources-export.rs b/crates/component-macro/tests/expanded/resources-export.rs index 2df3c228fa37..14981a1039b4 100644 --- a/crates/component-macro/tests/expanded/resources-export.rs +++ b/crates/component-macro/tests/expanded/resources-export.rs @@ -18,7 +18,7 @@ impl Clone for WPre { } } } -impl<_T> WPre<_T> { +impl<_T: 'static> WPre<_T> { /// Creates a new copy of `WPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -199,7 +199,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; WPre::new(pre)?.instantiate(store) } @@ -249,7 +252,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::anyhow; pub enum Y {} - pub trait HostY { + pub trait HostY: Sized { fn drop( &mut self, rep: wasmtime::component::Resource, @@ -263,22 +266,26 @@ pub mod foo { HostY::drop(*self, rep) } } - pub trait Host: HostY {} + pub trait Host: HostY + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/transitive-import")?; inst.resource( @@ -432,7 +439,10 @@ pub mod exports { pub fn call_constructor( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -446,7 +456,10 @@ pub mod exports { pub fn call_static_a( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -461,7 +474,10 @@ pub mod exports { &self, mut store: S, arg0: wasmtime::component::ResourceAny, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (wasmtime::component::ResourceAny,), @@ -602,7 +618,10 @@ pub mod exports { &self, mut store: S, arg0: wasmtime::component::Resource, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (wasmtime::component::Resource,), @@ -616,7 +635,10 @@ pub mod exports { pub fn call_static_a( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -632,7 +654,10 @@ pub mod exports { mut store: S, arg0: wasmtime::component::ResourceAny, arg1: wasmtime::component::Resource, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< ( @@ -747,7 +772,10 @@ pub mod exports { pub fn call_constructor( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -861,7 +889,10 @@ pub mod exports { &self, mut store: S, arg0: wasmtime::component::ResourceAny, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (wasmtime::component::ResourceAny,), diff --git a/crates/component-macro/tests/expanded/resources-export_async.rs b/crates/component-macro/tests/expanded/resources-export_async.rs index b5b3978dbb5e..e951db65163b 100644 --- a/crates/component-macro/tests/expanded/resources-export_async.rs +++ b/crates/component-macro/tests/expanded/resources-export_async.rs @@ -18,7 +18,7 @@ impl Clone for WPre { } } } -impl<_T> WPre<_T> { +impl<_T: Send + 'static> WPre<_T> { /// Creates a new copy of `WPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> WPre<_T> { 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) @@ -204,7 +201,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; WPre::new(pre)?.instantiate_async(store).await @@ -257,7 +254,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Y {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostY { + pub trait HostY: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -272,22 +269,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostY {} + pub trait Host: Send + HostY + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -450,7 +451,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -469,7 +470,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -489,7 +490,7 @@ pub mod exports { arg0: wasmtime::component::ResourceAny, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -635,7 +636,7 @@ pub mod exports { arg0: wasmtime::component::Resource, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -654,7 +655,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -675,7 +676,7 @@ pub mod exports { arg1: wasmtime::component::Resource, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -795,7 +796,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -914,7 +915,7 @@ pub mod exports { arg0: wasmtime::component::ResourceAny, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 219148f3870e..69f31a9db0cf 100644 --- a/crates/component-macro/tests/expanded/resources-export_tracing_async.rs +++ b/crates/component-macro/tests/expanded/resources-export_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for WPre { } } } -impl<_T> WPre<_T> { +impl<_T: Send + 'static> WPre<_T> { /// Creates a new copy of `WPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> WPre<_T> { 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) @@ -204,7 +201,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; WPre::new(pre)?.instantiate_async(store).await @@ -257,7 +254,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Y {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostY { + pub trait HostY: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -272,22 +269,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostY {} + pub trait Host: Send + HostY + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -450,7 +451,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -478,7 +479,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -507,7 +508,7 @@ pub mod exports { arg0: wasmtime::component::ResourceAny, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -662,7 +663,7 @@ pub mod exports { arg0: wasmtime::component::Resource, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -690,7 +691,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -721,7 +722,7 @@ pub mod exports { arg1: wasmtime::component::Resource, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -851,7 +852,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -979,7 +980,7 @@ pub mod exports { arg0: wasmtime::component::ResourceAny, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/resources-import.rs b/crates/component-macro/tests/expanded/resources-import.rs index cbea8b03f851..57b01dbee424 100644 --- a/crates/component-macro/tests/expanded/resources-import.rs +++ b/crates/component-macro/tests/expanded/resources-import.rs @@ -1,5 +1,5 @@ pub enum WorldResource {} -pub trait HostWorldResource { +pub trait HostWorldResource: Sized { fn new(&mut self) -> wasmtime::component::Resource; fn foo(&mut self, self_: wasmtime::component::Resource) -> (); fn static_foo(&mut self) -> (); @@ -45,7 +45,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -229,7 +229,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -321,7 +324,10 @@ const _: () = { pub fn call_some_world_func2( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -346,7 +352,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::anyhow; pub enum Bar {} - pub trait HostBar { + pub trait HostBar: Sized { fn new(&mut self) -> wasmtime::component::Resource; fn static_a(&mut self) -> u32; fn method_a(&mut self, self_: wasmtime::component::Resource) -> u32; @@ -430,7 +436,7 @@ pub mod foo { 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 ); }; - pub trait Host: HostBar { + pub trait Host: HostBar + Sized { fn bar_own_arg(&mut self, x: wasmtime::component::Resource) -> (); fn bar_borrow_arg( &mut self, @@ -492,19 +498,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/resources")?; inst.resource( @@ -862,7 +872,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::anyhow; pub enum A {} - pub trait HostA { + pub trait HostA: Sized { fn drop( &mut self, rep: wasmtime::component::Resource, @@ -876,22 +886,26 @@ pub mod foo { HostA::drop(*self, rep) } } - pub trait Host: HostA {} + pub trait Host: HostA + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/long-use-chain1")?; inst.resource( @@ -925,19 +939,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/long-use-chain2")?; Ok(()) @@ -961,19 +979,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/long-use-chain3")?; Ok(()) @@ -999,19 +1021,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/long-use-chain4")?; inst.func_wrap( @@ -1044,7 +1070,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::anyhow; pub enum Foo {} - pub trait HostFoo { + pub trait HostFoo: Sized { fn drop( &mut self, rep: wasmtime::component::Resource, @@ -1058,22 +1084,26 @@ pub mod foo { HostFoo::drop(*self, rep) } } - pub trait Host: HostFoo {} + pub trait Host: HostFoo + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker .instance("foo:foo/transitive-interface-with-resource")?; @@ -1199,7 +1229,10 @@ pub mod exports { &self, mut store: S, arg0: wasmtime::component::Resource, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (wasmtime::component::Resource,), diff --git a/crates/component-macro/tests/expanded/resources-import_async.rs b/crates/component-macro/tests/expanded/resources-import_async.rs index ec3f1308dd59..60ba22ce307a 100644 --- a/crates/component-macro/tests/expanded/resources-import_async.rs +++ b/crates/component-macro/tests/expanded/resources-import_async.rs @@ -1,6 +1,6 @@ pub enum WorldResource {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostWorldResource { +pub trait HostWorldResource: Sized { async fn new(&mut self) -> wasmtime::component::Resource; async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn static_foo(&mut self) -> (); @@ -46,7 +46,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -74,10 +74,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -236,7 +233,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -347,7 +344,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -374,7 +371,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Bar {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBar { + pub trait HostBar: Sized { async fn new(&mut self) -> wasmtime::component::Resource; async fn static_a(&mut self) -> u32; async fn method_a( @@ -462,7 +459,7 @@ pub mod foo { ); }; #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { + pub trait Host: Send + HostBar + Sized { async fn bar_own_arg( &mut self, x: wasmtime::component::Resource, @@ -529,19 +526,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -956,7 +957,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum A {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostA { + pub trait HostA: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -971,22 +972,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostA {} + pub trait Host: Send + HostA + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1028,19 +1033,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1069,19 +1078,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1112,19 +1125,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1164,7 +1181,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Foo {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFoo { + pub trait HostFoo: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -1179,22 +1196,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostFoo {} + pub trait Host: Send + HostFoo + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1329,7 +1350,7 @@ pub mod exports { arg0: wasmtime::component::Resource, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 51080a17dc50..851dfb3189f5 100644 --- a/crates/component-macro/tests/expanded/resources-import_tracing_async.rs +++ b/crates/component-macro/tests/expanded/resources-import_tracing_async.rs @@ -1,6 +1,6 @@ pub enum WorldResource {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostWorldResource { +pub trait HostWorldResource: Sized { async fn new(&mut self) -> wasmtime::component::Resource; async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn static_foo(&mut self) -> (); @@ -46,7 +46,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -74,10 +74,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -236,7 +233,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -402,7 +399,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -437,7 +434,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Bar {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBar { + pub trait HostBar: Sized { async fn new(&mut self) -> wasmtime::component::Resource; async fn static_a(&mut self) -> u32; async fn method_a( @@ -525,7 +522,7 @@ pub mod foo { ); }; #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { + pub trait Host: Send + HostBar + Sized { async fn bar_own_arg( &mut self, x: wasmtime::component::Resource, @@ -592,19 +589,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1347,7 +1348,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum A {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostA { + pub trait HostA: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -1362,22 +1363,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostA {} + pub trait Host: Send + HostA + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1419,19 +1424,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1460,19 +1469,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1503,19 +1516,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1568,7 +1585,7 @@ pub mod foo { use wasmtime::component::__internal::anyhow; pub enum Foo {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostFoo { + pub trait HostFoo: Sized { async fn drop( &mut self, rep: wasmtime::component::Resource, @@ -1583,22 +1600,26 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostFoo {} + pub trait Host: Send + HostFoo + Sized {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1733,7 +1754,7 @@ pub mod exports { arg0: wasmtime::component::Resource, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/share-types.rs b/crates/component-macro/tests/expanded/share-types.rs index 2e7bd33dfa35..904067c24e3b 100644 --- a/crates/component-macro/tests/expanded/share-types.rs +++ b/crates/component-macro/tests/expanded/share-types.rs @@ -18,7 +18,7 @@ impl Clone for HttpInterfacePre { } } } -impl<_T> HttpInterfacePre<_T> { +impl<_T: 'static> HttpInterfacePre<_T> { /// Creates a new copy of `HttpInterfacePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; HttpInterfacePre::new(pre)?.instantiate(store) } @@ -228,19 +231,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/http-types")?; Ok(()) @@ -277,19 +284,20 @@ pub mod http_fetch { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host GetHost<&'a mut T, T, Host: Host>>( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("http-fetch")?; inst.func_wrap( @@ -413,7 +421,10 @@ pub mod exports { &self, mut store: S, arg0: &Request, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&Request,), diff --git a/crates/component-macro/tests/expanded/share-types_async.rs b/crates/component-macro/tests/expanded/share-types_async.rs index d4ae2459ad93..916221dccc9f 100644 --- a/crates/component-macro/tests/expanded/share-types_async.rs +++ b/crates/component-macro/tests/expanded/share-types_async.rs @@ -18,7 +18,7 @@ impl Clone for HttpInterfacePre { } } } -impl<_T> HttpInterfacePre<_T> { +impl<_T: Send + 'static> HttpInterfacePre<_T> { /// Creates a new copy of `HttpInterfacePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> HttpInterfacePre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; HttpInterfacePre::new(pre)?.instantiate_async(store).await @@ -236,19 +233,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -290,19 +291,23 @@ pub mod http_fetch { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -434,7 +439,7 @@ pub mod exports { arg0: &Request, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 a6301a0bc381..d14b78c296ff 100644 --- a/crates/component-macro/tests/expanded/share-types_tracing_async.rs +++ b/crates/component-macro/tests/expanded/share-types_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for HttpInterfacePre { } } } -impl<_T> HttpInterfacePre<_T> { +impl<_T: Send + 'static> HttpInterfacePre<_T> { /// Creates a new copy of `HttpInterfacePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> HttpInterfacePre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; HttpInterfacePre::new(pre)?.instantiate_async(store).await @@ -236,19 +233,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -290,19 +291,23 @@ pub mod http_fetch { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -450,7 +455,7 @@ pub mod exports { arg0: &Request, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/simple-functions.rs b/crates/component-macro/tests/expanded/simple-functions.rs index 16274afb8f98..6f50a15f646e 100644 --- a/crates/component-macro/tests/expanded/simple-functions.rs +++ b/crates/component-macro/tests/expanded/simple-functions.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -196,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/simple")?; inst.func_wrap( @@ -427,7 +434,10 @@ pub mod exports { pub fn call_f1( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -442,7 +452,10 @@ pub mod exports { &self, mut store: S, arg0: u32, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u32,), @@ -458,7 +471,10 @@ pub mod exports { mut store: S, arg0: u32, arg1: u32, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u32, u32), @@ -472,7 +488,10 @@ pub mod exports { pub fn call_f4( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -486,7 +505,10 @@ pub mod exports { pub fn call_f5( &self, mut store: S, - ) -> wasmtime::Result<(u32, u32)> { + ) -> wasmtime::Result<(u32, u32)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -503,7 +525,10 @@ pub mod exports { arg0: u32, arg1: u32, arg2: u32, - ) -> wasmtime::Result<(u32, u32, u32)> { + ) -> wasmtime::Result<(u32, u32, u32)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (u32, u32, u32), diff --git a/crates/component-macro/tests/expanded/simple-functions_async.rs b/crates/component-macro/tests/expanded/simple-functions_async.rs index 37ac5a0edb0a..74be9afb343b 100644 --- a/crates/component-macro/tests/expanded/simple-functions_async.rs +++ b/crates/component-macro/tests/expanded/simple-functions_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -204,19 +201,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -453,7 +454,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -471,7 +472,7 @@ pub mod exports { arg0: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -492,7 +493,7 @@ pub mod exports { arg1: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -511,7 +512,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -530,7 +531,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(u32, u32)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -552,7 +553,7 @@ pub mod exports { arg2: u32, ) -> wasmtime::Result<(u32, u32, u32)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 2b7bc0d8e575..35fe7a591396 100644 --- a/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-functions_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -204,19 +201,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -541,7 +542,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -570,7 +571,7 @@ pub mod exports { arg0: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -600,7 +601,7 @@ pub mod exports { arg1: u32, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -628,7 +629,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -656,7 +657,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<(u32, u32)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -687,7 +688,7 @@ pub mod exports { arg2: u32, ) -> wasmtime::Result<(u32, u32, u32)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/simple-lists.rs b/crates/component-macro/tests/expanded/simple-lists.rs index 0215d464ac38..1012a973cf85 100644 --- a/crates/component-macro/tests/expanded/simple-lists.rs +++ b/crates/component-macro/tests/expanded/simple-lists.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate(store) } @@ -213,19 +216,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/simple-lists")?; inst.func_wrap( @@ -464,7 +471,10 @@ pub mod exports { &self, mut store: S, arg0: &[u32], - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u32],), @@ -478,7 +488,10 @@ pub mod exports { pub fn call_simple_list2( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -499,7 +512,10 @@ pub mod exports { wasmtime::component::__internal::Vec, wasmtime::component::__internal::Vec, ), - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[u32], &[u32]), @@ -523,7 +539,10 @@ pub mod exports { wasmtime::component::__internal::Vec< wasmtime::component::__internal::Vec, >, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&[wasmtime::component::__internal::Vec],), diff --git a/crates/component-macro/tests/expanded/simple-lists_async.rs b/crates/component-macro/tests/expanded/simple-lists_async.rs index a2d87ff226d0..660826a06b9c 100644 --- a/crates/component-macro/tests/expanded/simple-lists_async.rs +++ b/crates/component-macro/tests/expanded/simple-lists_async.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: Send + 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> MyWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate_async(store).await @@ -223,19 +220,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -490,7 +491,7 @@ pub mod exports { arg0: &[u32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -509,7 +510,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -535,7 +536,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -564,7 +565,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 2b654ca9410c..23b06b899507 100644 --- a/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-lists_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: Send + 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> MyWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate_async(store).await @@ -223,19 +220,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -551,7 +552,7 @@ pub mod exports { arg0: &[u32], ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -579,7 +580,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -614,7 +615,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -652,7 +653,7 @@ pub mod exports { >, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/simple-wasi.rs b/crates/component-macro/tests/expanded/simple-wasi.rs index 96d4a0bc2a94..ec1c062eecc1 100644 --- a/crates/component-macro/tests/expanded/simple-wasi.rs +++ b/crates/component-macro/tests/expanded/simple-wasi.rs @@ -18,7 +18,7 @@ impl Clone for WasiPre { } } } -impl<_T> WasiPre<_T> { +impl<_T: 'static> WasiPre<_T> { /// Creates a new copy of `WasiPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; WasiPre::new(pre)?.instantiate(store) } @@ -241,19 +244,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/wasi-filesystem")?; inst.func_wrap( @@ -299,19 +306,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/wall-clock")?; Ok(()) diff --git a/crates/component-macro/tests/expanded/simple-wasi_async.rs b/crates/component-macro/tests/expanded/simple-wasi_async.rs index a3c30802728d..03b16114ff54 100644 --- a/crates/component-macro/tests/expanded/simple-wasi_async.rs +++ b/crates/component-macro/tests/expanded/simple-wasi_async.rs @@ -18,7 +18,7 @@ impl Clone for WasiPre { } } } -impl<_T> WasiPre<_T> { +impl<_T: Send + 'static> WasiPre<_T> { /// Creates a new copy of `WasiPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> WasiPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; WasiPre::new(pre)?.instantiate_async(store).await @@ -249,19 +246,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -316,19 +317,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 92aa99a0ca4b..c381bc6cd898 100644 --- a/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs +++ b/crates/component-macro/tests/expanded/simple-wasi_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for WasiPre { } } } -impl<_T> WasiPre<_T> { +impl<_T: Send + 'static> WasiPre<_T> { /// Creates a new copy of `WasiPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> WasiPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; WasiPre::new(pre)?.instantiate_async(store).await @@ -249,19 +246,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -342,19 +343,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/small-anonymous.rs b/crates/component-macro/tests/expanded/small-anonymous.rs index baa6463b58e8..83f3c7366997 100644 --- a/crates/component-macro/tests/expanded/small-anonymous.rs +++ b/crates/component-macro/tests/expanded/small-anonymous.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -238,19 +241,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/anon")?; inst.func_wrap( @@ -431,7 +438,10 @@ pub mod exports { mut store: S, ) -> wasmtime::Result< Result, Error>, - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/small-anonymous_async.rs b/crates/component-macro/tests/expanded/small-anonymous_async.rs index bdf7af84c093..a631281bf344 100644 --- a/crates/component-macro/tests/expanded/small-anonymous_async.rs +++ b/crates/component-macro/tests/expanded/small-anonymous_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -246,19 +243,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -447,7 +448,7 @@ pub mod exports { Result, Error>, > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< 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 4d3526379cb9..af66bc5be64b 100644 --- a/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs +++ b/crates/component-macro/tests/expanded/small-anonymous_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -246,19 +243,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -460,7 +461,7 @@ pub mod exports { Result, Error>, > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/smoke-default.rs b/crates/component-macro/tests/expanded/smoke-default.rs index e9ab3af1d8d4..7e2120dc5626 100644 --- a/crates/component-macro/tests/expanded/smoke-default.rs +++ b/crates/component-macro/tests/expanded/smoke-default.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -170,7 +173,10 @@ const _: () = { pub fn call_y( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) }; diff --git a/crates/component-macro/tests/expanded/smoke-default_async.rs b/crates/component-macro/tests/expanded/smoke-default_async.rs index 30668f31d298..b44a971a5d8a 100644 --- a/crates/component-macro/tests/expanded/smoke-default_async.rs +++ b/crates/component-macro/tests/expanded/smoke-default_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -178,7 +175,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) 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 96f7792ceb76..de40b3c5991e 100644 --- a/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke-default_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -178,7 +175,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/smoke-export.rs b/crates/component-macro/tests/expanded/smoke-export.rs index 9a9f8a25b18e..03b7ebd47685 100644 --- a/crates/component-macro/tests/expanded/smoke-export.rs +++ b/crates/component-macro/tests/expanded/smoke-export.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -250,7 +253,10 @@ pub mod exports { pub fn call_y( &self, mut store: S, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) }; diff --git a/crates/component-macro/tests/expanded/smoke-export_async.rs b/crates/component-macro/tests/expanded/smoke-export_async.rs index e21b8232c855..054130bbff4c 100644 --- a/crates/component-macro/tests/expanded/smoke-export_async.rs +++ b/crates/component-macro/tests/expanded/smoke-export_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -258,7 +255,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) 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 9427cbcaa928..6dd91634d8b1 100644 --- a/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke-export_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -258,7 +255,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/smoke.rs b/crates/component-macro/tests/expanded/smoke.rs index f455ece5d3f7..35554d752ceb 100644 --- a/crates/component-macro/tests/expanded/smoke.rs +++ b/crates/component-macro/tests/expanded/smoke.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -176,19 +179,20 @@ pub mod imports { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host GetHost<&'a mut T, T, Host: Host>>( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("imports")?; inst.func_wrap( diff --git a/crates/component-macro/tests/expanded/smoke_async.rs b/crates/component-macro/tests/expanded/smoke_async.rs index fb7bc94f54f6..9d043493096c 100644 --- a/crates/component-macro/tests/expanded/smoke_async.rs +++ b/crates/component-macro/tests/expanded/smoke_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod imports { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/smoke_tracing_async.rs b/crates/component-macro/tests/expanded/smoke_tracing_async.rs index 038df8017ab0..20d2137493b7 100644 --- a/crates/component-macro/tests/expanded/smoke_tracing_async.rs +++ b/crates/component-macro/tests/expanded/smoke_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -184,19 +181,23 @@ pub mod imports { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/strings.rs b/crates/component-macro/tests/expanded/strings.rs index 743d69f5044c..bc34c8ae871d 100644 --- a/crates/component-macro/tests/expanded/strings.rs +++ b/crates/component-macro/tests/expanded/strings.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -197,19 +200,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/strings")?; inst.func_wrap( @@ -384,7 +391,10 @@ pub mod exports { &self, mut store: S, arg0: &str, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&str,), @@ -398,7 +408,10 @@ pub mod exports { pub fn call_b( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -414,7 +427,10 @@ pub mod exports { mut store: S, arg0: &str, arg1: &str, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&str, &str), diff --git a/crates/component-macro/tests/expanded/strings_async.rs b/crates/component-macro/tests/expanded/strings_async.rs index b598be395c92..08ab846540f0 100644 --- a/crates/component-macro/tests/expanded/strings_async.rs +++ b/crates/component-macro/tests/expanded/strings_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -404,7 +405,7 @@ pub mod exports { arg0: &str, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -423,7 +424,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -444,7 +445,7 @@ pub mod exports { arg1: &str, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/strings_tracing_async.rs b/crates/component-macro/tests/expanded/strings_tracing_async.rs index deb2db8761ed..1fb989feb408 100644 --- a/crates/component-macro/tests/expanded/strings_tracing_async.rs +++ b/crates/component-macro/tests/expanded/strings_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -449,7 +450,7 @@ pub mod exports { arg0: &str, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -477,7 +478,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -507,7 +508,7 @@ pub mod exports { arg1: &str, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/unstable-features.rs b/crates/component-macro/tests/expanded/unstable-features.rs index c252a541394d..d6900dd1fa4d 100644 --- a/crates/component-macro/tests/expanded/unstable-features.rs +++ b/crates/component-macro/tests/expanded/unstable-features.rs @@ -62,7 +62,7 @@ impl LinkOptions { } } pub enum Baz {} -pub trait HostBaz { +pub trait HostBaz: Sized { fn foo(&mut self, self_: wasmtime::component::Resource) -> (); fn drop(&mut self, rep: wasmtime::component::Resource) -> wasmtime::Result<()>; } @@ -111,7 +111,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -255,7 +255,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate(store) } @@ -384,7 +387,7 @@ pub mod foo { } } pub enum Bar {} - pub trait HostBar { + pub trait HostBar: Sized { fn foo(&mut self, self_: wasmtime::component::Resource) -> (); fn drop( &mut self, @@ -402,25 +405,29 @@ pub mod foo { HostBar::drop(*self, rep) } } - pub trait Host: HostBar { + pub trait Host: HostBar + Sized { fn foo(&mut self) -> (); } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { if options.experimental_interface { let mut inst = linker.instance("foo:foo/the-interface")?; diff --git a/crates/component-macro/tests/expanded/unstable-features_async.rs b/crates/component-macro/tests/expanded/unstable-features_async.rs index 3580c845da5e..517e6005438a 100644 --- a/crates/component-macro/tests/expanded/unstable-features_async.rs +++ b/crates/component-macro/tests/expanded/unstable-features_async.rs @@ -63,7 +63,7 @@ impl LinkOptions { } pub enum Baz {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostBaz { +pub trait HostBaz: Sized { async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn drop( &mut self, @@ -118,7 +118,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -146,10 +146,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -268,7 +265,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -410,7 +407,7 @@ pub mod foo { } pub enum Bar {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBar { + pub trait HostBar: Sized { async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn drop( &mut self, @@ -432,25 +429,29 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { + pub trait Host: Send + HostBar + Sized { async fn foo(&mut self) -> (); } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 676c088e0589..ef811882cf13 100644 --- a/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs +++ b/crates/component-macro/tests/expanded/unstable-features_tracing_async.rs @@ -63,7 +63,7 @@ impl LinkOptions { } pub enum Baz {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] -pub trait HostBaz { +pub trait HostBaz: Sized { async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn drop( &mut self, @@ -118,7 +118,7 @@ impl Clone for TheWorldPre { } } } -impl<_T> TheWorldPre<_T> { +impl<_T: Send + 'static> TheWorldPre<_T> { /// Creates a new copy of `TheWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -146,10 +146,7 @@ impl<_T> TheWorldPre<_T> { 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) @@ -268,7 +265,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; TheWorldPre::new(pre)?.instantiate_async(store).await @@ -439,7 +436,7 @@ pub mod foo { } pub enum Bar {} #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait HostBar { + pub trait HostBar: Sized { async fn foo(&mut self, self_: wasmtime::component::Resource) -> (); async fn drop( &mut self, @@ -461,25 +458,29 @@ pub mod foo { } } #[wasmtime::component::__internal::trait_variant_make(::core::marker::Send)] - pub trait Host: Send + HostBar { + pub trait Host: Send + HostBar + Sized { async fn foo(&mut self) -> (); } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/unversioned-foo.rs b/crates/component-macro/tests/expanded/unversioned-foo.rs index 20b394cf1bfb..facb0a7058f2 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo.rs @@ -18,7 +18,7 @@ impl Clone for NopePre { } } } -impl<_T> NopePre<_T> { +impl<_T: 'static> NopePre<_T> { /// Creates a new copy of `NopePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; NopePre::new(pre)?.instantiate(store) } @@ -206,19 +209,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/a")?; inst.func_wrap( diff --git a/crates/component-macro/tests/expanded/unversioned-foo_async.rs b/crates/component-macro/tests/expanded/unversioned-foo_async.rs index 7f3947b8a439..96999846334e 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo_async.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo_async.rs @@ -18,7 +18,7 @@ impl Clone for NopePre { } } } -impl<_T> NopePre<_T> { +impl<_T: Send + 'static> NopePre<_T> { /// Creates a new copy of `NopePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> NopePre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; NopePre::new(pre)?.instantiate_async(store).await @@ -214,19 +211,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 5775c1d239c1..0af6539b556c 100644 --- a/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs +++ b/crates/component-macro/tests/expanded/unversioned-foo_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for NopePre { } } } -impl<_T> NopePre<_T> { +impl<_T: Send + 'static> NopePre<_T> { /// Creates a new copy of `NopePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> NopePre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; NopePre::new(pre)?.instantiate_async(store).await @@ -214,19 +211,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/use-paths.rs b/crates/component-macro/tests/expanded/use-paths.rs index 42e5e053aebc..44131d990a60 100644 --- a/crates/component-macro/tests/expanded/use-paths.rs +++ b/crates/component-macro/tests/expanded/use-paths.rs @@ -18,7 +18,7 @@ impl Clone for DPre { } } } -impl<_T> DPre<_T> { +impl<_T: 'static> DPre<_T> { /// Creates a new copy of `DPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -142,7 +142,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; DPre::new(pre)?.instantiate(store) } @@ -196,19 +199,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/a")?; inst.func_wrap( @@ -250,19 +257,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/b")?; inst.func_wrap( @@ -304,19 +315,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/c")?; inst.func_wrap( @@ -360,19 +375,20 @@ pub mod d { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host GetHost<&'a mut T, T, Host: Host>>( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("d")?; inst.func_wrap( diff --git a/crates/component-macro/tests/expanded/use-paths_async.rs b/crates/component-macro/tests/expanded/use-paths_async.rs index 256e28bb4c2f..65d0fdc86304 100644 --- a/crates/component-macro/tests/expanded/use-paths_async.rs +++ b/crates/component-macro/tests/expanded/use-paths_async.rs @@ -18,7 +18,7 @@ impl Clone for DPre { } } } -impl<_T> DPre<_T> { +impl<_T: Send + 'static> DPre<_T> { /// Creates a new copy of `DPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> DPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; DPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -266,19 +267,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -327,19 +332,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -390,19 +399,23 @@ pub mod d { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 c9dd4cd453c5..f38030d3a6d7 100644 --- a/crates/component-macro/tests/expanded/use-paths_tracing_async.rs +++ b/crates/component-macro/tests/expanded/use-paths_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for DPre { } } } -impl<_T> DPre<_T> { +impl<_T: Send + 'static> DPre<_T> { /// Creates a new copy of `DPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> DPre<_T> { 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) @@ -147,7 +144,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; DPre::new(pre)?.instantiate_async(store).await @@ -205,19 +202,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -279,19 +280,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -353,19 +358,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -429,19 +438,23 @@ pub mod d { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/variants.rs b/crates/component-macro/tests/expanded/variants.rs index 3e67dc0d976d..68af829ef994 100644 --- a/crates/component-macro/tests/expanded/variants.rs +++ b/crates/component-macro/tests/expanded/variants.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -152,7 +152,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate(store) } @@ -532,19 +535,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/variants")?; inst.func_wrap( @@ -1613,7 +1620,10 @@ pub mod exports { &self, mut store: S, arg0: E1, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (E1,), @@ -1627,7 +1637,10 @@ pub mod exports { pub fn call_e1_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1642,7 +1655,10 @@ pub mod exports { &self, mut store: S, arg0: &V1, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&V1,), @@ -1656,7 +1672,10 @@ pub mod exports { pub fn call_v1_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1671,7 +1690,10 @@ pub mod exports { &self, mut store: S, arg0: bool, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (bool,), @@ -1685,7 +1707,10 @@ pub mod exports { pub fn call_bool_result( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1705,7 +1730,10 @@ pub mod exports { arg3: Option, arg4: Option, arg5: Option>, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< ( @@ -1739,7 +1767,10 @@ pub mod exports { Option, Option>, ), - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1770,7 +1801,10 @@ pub mod exports { arg5: Casts6, ) -> wasmtime::Result< (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), @@ -1794,7 +1828,10 @@ pub mod exports { arg3: Result<(), ()>, arg4: Result, arg5: Result<&str, &[u8]>, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< ( @@ -1831,7 +1868,10 @@ pub mod exports { wasmtime::component::__internal::Vec, >, ), - > { + > + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1857,7 +1897,10 @@ pub mod exports { pub fn call_return_result_sugar( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1871,7 +1914,10 @@ pub mod exports { pub fn call_return_result_sugar2( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1885,7 +1931,10 @@ pub mod exports { pub fn call_return_result_sugar3( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1899,7 +1948,10 @@ pub mod exports { pub fn call_return_result_sugar4( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1913,7 +1965,10 @@ pub mod exports { pub fn call_return_option_sugar( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1927,7 +1982,10 @@ pub mod exports { pub fn call_return_option_sugar2( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1941,7 +1999,10 @@ pub mod exports { pub fn call_result_simple( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1956,7 +2017,10 @@ pub mod exports { &self, mut store: S, arg0: &IsClone, - ) -> wasmtime::Result<()> { + ) -> wasmtime::Result<()> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (&IsClone,), @@ -1970,7 +2034,10 @@ pub mod exports { pub fn call_is_clone_return( &self, mut store: S, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1984,7 +2051,10 @@ pub mod exports { pub fn call_return_named_option( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), @@ -1998,7 +2068,10 @@ pub mod exports { pub fn call_return_named_result( &self, mut store: S, - ) -> wasmtime::Result> { + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::< (), diff --git a/crates/component-macro/tests/expanded/variants_async.rs b/crates/component-macro/tests/expanded/variants_async.rs index 371dc5cb517c..fe4082549e5e 100644 --- a/crates/component-macro/tests/expanded/variants_async.rs +++ b/crates/component-macro/tests/expanded/variants_async.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: Send + 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> MyWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate_async(store).await @@ -540,19 +537,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1674,7 +1675,7 @@ pub mod exports { arg0: E1, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1693,7 +1694,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1713,7 +1714,7 @@ pub mod exports { arg0: &V1, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1732,7 +1733,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1752,7 +1753,7 @@ pub mod exports { arg0: bool, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1771,7 +1772,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1796,7 +1797,7 @@ pub mod exports { arg5: Option>, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1834,7 +1835,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1870,7 +1871,7 @@ pub mod exports { (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1898,7 +1899,7 @@ pub mod exports { arg5: Result<&str, &[u8]>, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1939,7 +1940,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1970,7 +1971,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -1989,7 +1990,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2008,7 +2009,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2027,7 +2028,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2046,7 +2047,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2065,7 +2066,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2084,7 +2085,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2104,7 +2105,7 @@ pub mod exports { arg0: &IsClone, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2123,7 +2124,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2142,7 +2143,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -2161,7 +2162,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< diff --git a/crates/component-macro/tests/expanded/variants_tracing_async.rs b/crates/component-macro/tests/expanded/variants_tracing_async.rs index 8f29d6b7884e..b67dab3319f4 100644 --- a/crates/component-macro/tests/expanded/variants_tracing_async.rs +++ b/crates/component-macro/tests/expanded/variants_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for MyWorldPre { } } } -impl<_T> MyWorldPre<_T> { +impl<_T: Send + 'static> MyWorldPre<_T> { /// Creates a new copy of `MyWorldPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> MyWorldPre<_T> { 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) @@ -157,7 +154,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; MyWorldPre::new(pre)?.instantiate_async(store).await @@ -540,19 +537,23 @@ pub mod foo { } pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -1998,7 +1999,7 @@ pub mod exports { arg0: E1, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2026,7 +2027,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2055,7 +2056,7 @@ pub mod exports { arg0: &V1, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2083,7 +2084,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2112,7 +2113,7 @@ pub mod exports { arg0: bool, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2140,7 +2141,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2174,7 +2175,7 @@ pub mod exports { arg5: Option>, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2221,7 +2222,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2266,7 +2267,7 @@ pub mod exports { (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2303,7 +2304,7 @@ pub mod exports { arg5: Result<&str, &[u8]>, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2353,7 +2354,7 @@ pub mod exports { ), > where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2393,7 +2394,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2421,7 +2422,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2449,7 +2450,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2477,7 +2478,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2505,7 +2506,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2533,7 +2534,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2561,7 +2562,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2590,7 +2591,7 @@ pub mod exports { arg0: &IsClone, ) -> wasmtime::Result<()> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2618,7 +2619,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2646,7 +2647,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -2674,7 +2675,7 @@ pub mod exports { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( diff --git a/crates/component-macro/tests/expanded/wat.rs b/crates/component-macro/tests/expanded/wat.rs index 14da6ff9efb6..4196504c6b9f 100644 --- a/crates/component-macro/tests/expanded/wat.rs +++ b/crates/component-macro/tests/expanded/wat.rs @@ -18,7 +18,7 @@ impl Clone for ExamplePre { } } } -impl<_T> ExamplePre<_T> { +impl<_T: 'static> ExamplePre<_T> { /// Creates a new copy of `ExamplePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -154,7 +154,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; ExamplePre::new(pre)?.instantiate(store) } diff --git a/crates/component-macro/tests/expanded/wat_async.rs b/crates/component-macro/tests/expanded/wat_async.rs index cad73611fe21..a5bf68e7a213 100644 --- a/crates/component-macro/tests/expanded/wat_async.rs +++ b/crates/component-macro/tests/expanded/wat_async.rs @@ -18,7 +18,7 @@ impl Clone for ExamplePre { } } } -impl<_T> ExamplePre<_T> { +impl<_T: Send + 'static> ExamplePre<_T> { /// Creates a new copy of `ExamplePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> ExamplePre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; ExamplePre::new(pre)?.instantiate_async(store).await diff --git a/crates/component-macro/tests/expanded/wat_tracing_async.rs b/crates/component-macro/tests/expanded/wat_tracing_async.rs index cad73611fe21..a5bf68e7a213 100644 --- a/crates/component-macro/tests/expanded/wat_tracing_async.rs +++ b/crates/component-macro/tests/expanded/wat_tracing_async.rs @@ -18,7 +18,7 @@ impl Clone for ExamplePre { } } } -impl<_T> ExamplePre<_T> { +impl<_T: Send + 'static> ExamplePre<_T> { /// Creates a new copy of `ExamplePre` bindings which can then /// be used to instantiate into a particular store. /// @@ -46,10 +46,7 @@ impl<_T> ExamplePre<_T> { 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) @@ -159,7 +156,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; ExamplePre::new(pre)?.instantiate_async(store).await diff --git a/crates/component-macro/tests/expanded/worlds-with-types.rs b/crates/component-macro/tests/expanded/worlds-with-types.rs index 2fd790395a13..544ce6a0e367 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types.rs @@ -43,7 +43,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -181,7 +181,10 @@ const _: () = { mut store: impl wasmtime::AsContextMut, component: &wasmtime::component::Component, linker: &wasmtime::component::Linker<_T>, - ) -> wasmtime::Result { + ) -> wasmtime::Result + where + _T: 'static, + { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate(store) } @@ -207,7 +210,10 @@ const _: () = { pub fn call_f( &self, mut store: S, - ) -> wasmtime::Result<(T, U, R)> { + ) -> wasmtime::Result<(T, U, R)> + where + ::Data: Send + 'static, + { let callee = unsafe { wasmtime::component::TypedFunc::<(), ((T, U, R),)>::new_unchecked(self.f) }; @@ -231,19 +237,23 @@ pub mod foo { pub trait Host {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host; } - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host, { type Host = O; } - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut inst = linker.instance("foo:foo/i")?; Ok(()) 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 ec1384650f3c..d5df30f21a0e 100644 --- a/crates/component-macro/tests/expanded/worlds-with-types_async.rs +++ b/crates/component-macro/tests/expanded/worlds-with-types_async.rs @@ -43,7 +43,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -71,10 +71,7 @@ impl<_T> FooPre<_T> { 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) @@ -186,7 +183,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -216,7 +213,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<(T, U, R)> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::<(), ((T, U, R),)>::new_unchecked(self.f) @@ -242,19 +239,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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 6c8e7b8d36ef..c2813e5c96fc 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 @@ -43,7 +43,7 @@ impl Clone for FooPre { } } } -impl<_T> FooPre<_T> { +impl<_T: Send + 'static> FooPre<_T> { /// Creates a new copy of `FooPre` bindings which can then /// be used to instantiate into a particular store. /// @@ -71,10 +71,7 @@ impl<_T> FooPre<_T> { 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) @@ -186,7 +183,7 @@ const _: () = { linker: &wasmtime::component::Linker<_T>, ) -> wasmtime::Result where - _T: Send, + _T: Send + 'static, { let pre = linker.instantiate_pre(component)?; FooPre::new(pre)?.instantiate_async(store).await @@ -216,7 +213,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result<(T, U, R)> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -250,19 +247,23 @@ pub mod foo { pub trait Host: Send {} pub trait GetHost< T, - >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host + Send; } - impl GetHost for F + 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( + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml index 635a4eec91e8..f20a91139cbd 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -46,3 +46,4 @@ gc = ["wasmtime-environ/gc"] gc-drc = ["gc", "wasmtime-environ/gc-drc"] gc-null = ["gc", "wasmtime-environ/gc-null"] threads = ["wasmtime-environ/threads"] + diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index c3c84ad6bfc8..0fd399ba3648 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -3,7 +3,7 @@ use crate::{compiler::Compiler, TRAP_ALWAYS, TRAP_CANNOT_ENTER, TRAP_INTERNAL_ASSERT}; use anyhow::Result; use cranelift_codegen::ir::condcodes::IntCC; -use cranelift_codegen::ir::{self, InstBuilder, MemFlags}; +use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value}; use cranelift_codegen::isa::{CallConv, TargetIsa}; use cranelift_frontend::FunctionBuilder; use std::any::Any; @@ -97,24 +97,448 @@ impl<'a> TrampolineCompiler<'a> { 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::TaskBackpressure { instance } => { + self.translate_task_backpressure_call(*instance) + } + Trampoline::TaskReturn => self.translate_task_return_call(), + Trampoline::TaskWait { + instance, + async_, + memory, + } => self.translate_task_wait_or_poll_call( + *instance, + *async_, + *memory, + self.offsets.task_wait(), + ), + Trampoline::TaskPoll { + instance, + async_, + memory, + } => self.translate_task_wait_or_poll_call( + *instance, + *async_, + *memory, + self.offsets.task_poll(), + ), + Trampoline::TaskYield { async_ } => self.translate_task_yield_call(*async_), + Trampoline::SubtaskDrop { instance } => self.translate_subtask_drop_call(*instance), + Trampoline::StreamNew { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.stream_new(), + Vec::new(), + ir::types::I64, + ), + Trampoline::StreamRead { ty, options } => { + if let Some(info) = self.flat_stream_element_info(*ty) { + self.translate_flat_stream_call( + *ty, + options, + self.offsets.flat_stream_read(), + &info, + ) + } else { + self.translate_future_or_stream_call( + ty.as_u32(), + Some(options), + self.offsets.stream_read(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ) + } + } + Trampoline::StreamWrite { ty, options } => { + if let Some(info) = self.flat_stream_element_info(*ty) { + self.translate_flat_stream_call( + *ty, + options, + self.offsets.flat_stream_write(), + &info, + ) + } else { + self.translate_future_or_stream_call( + ty.as_u32(), + Some(options), + self.offsets.stream_write(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ) + } + } + Trampoline::StreamCancelRead { ty, async_ } => { + self.translate_cancel_call(ty.as_u32(), *async_, self.offsets.stream_cancel_read()) + } + Trampoline::StreamCancelWrite { ty, async_ } => { + self.translate_cancel_call(ty.as_u32(), *async_, self.offsets.stream_cancel_write()) + } + Trampoline::StreamCloseReadable { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.stream_close_readable(), + vec![ir::AbiParam::new(ir::types::I32)], + ir::types::I8, + ), + Trampoline::StreamCloseWritable { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.stream_close_writable(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I8, + ), + Trampoline::FutureNew { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.future_new(), + Vec::new(), + ir::types::I64, + ), + Trampoline::FutureRead { ty, options } => self.translate_future_or_stream_call( + ty.as_u32(), + Some(&options), + self.offsets.future_read(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ), + Trampoline::FutureWrite { ty, options } => self.translate_future_or_stream_call( + ty.as_u32(), + Some(options), + self.offsets.future_write(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ), + Trampoline::FutureCancelRead { ty, async_ } => { + self.translate_cancel_call(ty.as_u32(), *async_, self.offsets.future_cancel_read()) + } + Trampoline::FutureCancelWrite { ty, async_ } => { + self.translate_cancel_call(ty.as_u32(), *async_, self.offsets.future_cancel_write()) + } + Trampoline::FutureCloseReadable { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.future_close_readable(), + vec![ir::AbiParam::new(ir::types::I32)], + ir::types::I8, + ), + Trampoline::FutureCloseWritable { ty } => self.translate_future_or_stream_call( + ty.as_u32(), + None, + self.offsets.future_close_writable(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I8, + ), + Trampoline::ErrorContextNew { ty, options } => self.translate_error_context_call( + *ty, + options, + self.offsets.error_context_new(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ), + Trampoline::ErrorContextDebugMessage { ty, options } => self + .translate_error_context_call( + *ty, + options, + self.offsets.error_context_debug_message(), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I8, + ), + Trampoline::ErrorContextDrop { ty } => self.translate_error_context_drop_call(*ty), Trampoline::ResourceTransferOwn => { - self.translate_resource_libcall(host::resource_transfer_own, |me, rets| { - rets[0] = me.raise_if_resource_trapped(rets[0]); + self.translate_host_libcall(host::resource_transfer_own, |me, rets| { + rets[0] = me.raise_if_i32_trapped(rets[0]); }) } Trampoline::ResourceTransferBorrow => { - self.translate_resource_libcall(host::resource_transfer_borrow, |me, rets| { - rets[0] = me.raise_if_resource_trapped(rets[0]); + self.translate_host_libcall(host::resource_transfer_borrow, |me, rets| { + rets[0] = me.raise_if_i32_trapped(rets[0]); }) } Trampoline::ResourceEnterCall => { - self.translate_resource_libcall(host::resource_enter_call, |_, _| {}) + self.translate_host_libcall(host::resource_enter_call, |_, _| {}) } Trampoline::ResourceExitCall => { - self.translate_resource_libcall(host::resource_exit_call, |me, rets| { + self.translate_host_libcall(host::resource_exit_call, |me, rets| { me.raise_if_host_trapped(rets.pop().unwrap()); }) } + Trampoline::AsyncEnterCall => { + let pointer_type = self.isa.pointer_type(); + self.translate_async_enter_or_exit( + self.offsets.async_enter(), + None, + vec![ + ir::AbiParam::new(pointer_type), + ir::AbiParam::new(pointer_type), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I8, + ) + } + Trampoline::AsyncExitCall(callback) => { + let pointer_type = self.isa.pointer_type(); + self.translate_async_enter_or_exit( + self.offsets.async_exit(), + Some(*callback), + vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(pointer_type), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ], + ir::types::I64, + ) + } + Trampoline::FutureTransfer => { + self.translate_host_libcall(host::future_transfer, |me, rets| { + rets[0] = me.raise_if_i32_trapped(rets[0]); + }) + } + Trampoline::StreamTransfer => { + self.translate_host_libcall(host::stream_transfer, |me, rets| { + rets[0] = me.raise_if_i32_trapped(rets[0]); + }) + } + Trampoline::ErrorContextTransfer => { + self.translate_host_libcall(host::error_context_transfer, |me, rets| { + rets[0] = me.raise_if_i32_trapped(rets[0]); + }) + } + } + } + + fn flat_stream_element_info(&self, ty: TypeStreamTableIndex) -> Option { + let payload = self.types[self.types[ty].ty].payload; + match payload { + InterfaceType::Bool + | InterfaceType::S8 + | InterfaceType::U8 + | InterfaceType::S16 + | InterfaceType::U16 + | InterfaceType::S32 + | InterfaceType::U32 + | InterfaceType::S64 + | InterfaceType::U64 + | InterfaceType::Float32 + | InterfaceType::Float64 + | InterfaceType::Char => Some(self.types.canonical_abi(&payload).clone()), + // TODO: Recursively check for other "flat" types (i.e. those without pointers or handles), + // e.g. `record`s, `variant`s, etc. which contain only flat types. + _ => None, + } + } + + 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 { + Abi::Wasm => { + let (ptr, len) = self.compiler.allocate_stack_array_and_spill_args( + wasm_func_ty, + &mut self.builder, + args, + ); + let len = self.builder.ins().iconst(pointer_type, i64::from(len)); + (ptr, len) + } + Abi::Array => { + let params = self.builder.func.dfg.block_params(self.block0); + (params[2], params[3]) + } + } + } + + fn translate_task_return_call(&mut self) { + 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 pointer_type = self.isa.pointer_type(); + 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 mut callee_args = Vec::new(); + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + + // vmctx: *mut VMComponentContext + host_sig.params.push(ir::AbiParam::new(pointer_type)); + callee_args.push(vmctx); + + let params = self.types[self.signature] + .unwrap_func() + .params() + .iter() + .map(|&v| { + Some(match v { + WasmValType::I32 => FlatType::I32, + WasmValType::I64 => FlatType::I64, + WasmValType::F32 => FlatType::F32, + WasmValType::F64 => FlatType::F64, + _ => return None, + }) + }) + .collect::>(); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + callee_args.push( + self.builder.ins().iconst( + ir::types::I32, + i64::from( + params + .and_then(|params| { + self.types + .get_task_return_type(&TypeTaskReturn { params }) + .map(|v| v.as_u32()) + }) + .unwrap_or(u32::MAX), + ), + ), + ); + + // storage: *mut ValRaw + host_sig.params.push(ir::AbiParam::new(pointer_type)); + callee_args.push(values_vec_ptr); + + // storage_len: usize + host_sig.params.push(ir::AbiParam::new(pointer_type)); + callee_args.push(values_vec_len); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + + // 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.task_return()).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &callee_args); + + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + + fn translate_async_enter_or_exit( + &mut self, + offset: u32, + callback: Option>, + params: Vec, + result: ir::types::Type, + ) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + + let mut callee_args = Vec::new(); + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + + // vmctx: *mut VMComponentContext + host_sig.params.push(ir::AbiParam::new(pointer_type)); + callee_args.push(vmctx); + + if let Some(callback) = callback { + // callback: *mut VMFuncRef + host_sig.params.push(ir::AbiParam::new(pointer_type)); + if let Some(callback) = callback { + callee_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_callback(callback)).unwrap(), + )); + } else { + callee_args.push(self.builder.ins().iconst(pointer_type, 0)); + } + } + + // remaining parameters + host_sig.params.extend(params); + callee_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(result)); + + // 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(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &callee_args); + + if result == ir::types::I64 { + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } else { + assert!(result == ir::types::I8); + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); } } @@ -157,10 +581,14 @@ impl<'a> TrampolineCompiler<'a> { instance, memory, realloc, + callback, post_return, string_encoding, + async_, } = *options; + assert!(callback.is_none()); + // vmctx: *mut VMComponentContext host_sig.params.push(ir::AbiParam::new(pointer_type)); callee_args.push(vmctx); @@ -182,6 +610,14 @@ impl<'a> TrampolineCompiler<'a> { .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( @@ -227,6 +663,14 @@ impl<'a> TrampolineCompiler<'a> { .iconst(ir::types::I8, i64::from(string_encoding as u8)), ); + // 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 }), + ); + // storage: *mut ValRaw host_sig.params.push(ir::AbiParam::new(pointer_type)); callee_args.push(values_vec_ptr); @@ -330,7 +774,7 @@ impl<'a> TrampolineCompiler<'a> { ); 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_resource_trapped(result); + let result = self.raise_if_i32_trapped(result); self.abi_store_results(&[result]); } @@ -359,7 +803,7 @@ impl<'a> TrampolineCompiler<'a> { ); 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_resource_trapped(result); + let result = self.raise_if_i32_trapped(result); self.abi_store_results(&[result]); } @@ -550,7 +994,7 @@ impl<'a> TrampolineCompiler<'a> { /// /// Only intended for simple trampolines and effectively acts as a bridge /// from the wasm abi to host. - fn translate_resource_libcall( + fn translate_host_libcall( &mut self, get_libcall: fn( &dyn TargetIsa, @@ -580,6 +1024,612 @@ impl<'a> TrampolineCompiler<'a> { self.builder.ins().return_(&results); } + fn translate_cancel_call(&mut self, ty: u32, async_: bool, offset: u32) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push(self.builder.ins().iconst(ir::types::I32, i64::from(ty))); + + // async_: bool + host_sig.params.push(ir::AbiParam::new(ir::types::I8)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I8, if async_ { 1 } else { 0 }), + ); + + // 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(offset).unwrap(), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I64)); + + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } + + fn translate_future_or_stream_call( + &mut self, + ty: u32, + options: Option<&CanonicalOptions>, + offset: u32, + params: Vec, + result: ir::types::Type, + ) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + if let Some(options) = options { + // memory: *mut VMMemoryDefinition + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_memory(options.memory.unwrap())).unwrap(), + )); + + // realloc: *mut VMFuncRef + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(match options.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), + }); + + // string_encoding: StringEncoding + host_sig.params.push(ir::AbiParam::new(ir::types::I8)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I8, i64::from(options.string_encoding as u8)), + ); + } + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push(self.builder.ins().iconst(ir::types::I32, i64::from(ty))); + + host_sig.params.extend(params); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(result)); + + // 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(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + if result == ir::types::I64 { + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } else { + assert!(result == ir::types::I8); + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + } + + fn translate_flat_stream_call( + &mut self, + ty: TypeStreamTableIndex, + options: &CanonicalOptions, + offset: u32, + info: &CanonicalAbiInfo, + ) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + // memory: *mut VMMemoryDefinition + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_memory(options.memory.unwrap())).unwrap(), + )); + + // realloc: *mut VMFuncRef + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(match options.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), + }); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(ty.as_u32())), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(info.size32)), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(info.align32)), + ); + + host_sig.params.extend(vec![ + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ir::AbiParam::new(ir::types::I32), + ]); + host_args.extend(args[2..].iter().copied()); + + host_sig + .returns + .extend(vec![ir::AbiParam::new(ir::types::I64)]); + + // 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(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } + + fn translate_error_context_call( + &mut self, + ty: TypeErrorContextTableIndex, + options: &CanonicalOptions, + offset: u32, + params: Vec, + result: ir::types::Type, + ) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + // memory: *mut VMMemoryDefinition + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_memory(options.memory.unwrap())).unwrap(), + )); + + // realloc: *mut VMFuncRef + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(match options.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), + }); + + // string_encoding: StringEncoding + host_sig.params.push(ir::AbiParam::new(ir::types::I8)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I8, i64::from(options.string_encoding as u8)), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(ty.as_u32())), + ); + + host_sig.params.extend(params); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(result)); + + // 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(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + if result == ir::types::I64 { + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } else { + assert!(result == ir::types::I8); + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + } + + fn translate_error_context_drop_call(&mut self, ty: TypeErrorContextTableIndex) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(ty.as_u32())), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + + // Load host function pointer from the vmcontext and then call that + // indirect function pointer with the list of arguments. + let offset = self.offsets.error_context_drop(); + let host_fn = self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + + fn translate_task_backpressure_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + + // 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.task_backpressure()).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + + fn translate_subtask_drop_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + + // 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.subtask_drop()).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + + fn translate_task_wait_or_poll_call( + &mut self, + caller_instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: RuntimeMemoryIndex, + offset: u32, + ) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I32, i64::from(caller_instance.as_u32())), + ); + + host_sig.params.push(ir::AbiParam::new(ir::types::I8)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I8, if async_ { 1 } else { 0 }), + ); + + // memory: *mut VMMemoryDefinition + host_sig.params.push(ir::AbiParam::new(pointer_type)); + host_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_memory(memory)).unwrap(), + )); + + host_sig.params.push(ir::AbiParam::new(ir::types::I32)); + host_args.extend(args[2..].iter().copied()); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I64)); + + // 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(offset).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let result = self.builder.func.dfg.inst_results(call)[0]; + let result = self.raise_if_i32_trapped(result); + self.abi_store_results(&[result]); + } + + fn translate_task_yield_call(&mut self, async_: bool) { + 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 pointer_type = self.isa.pointer_type(); + let args = self.builder.func.dfg.block_params(self.block0).to_vec(); + let vmctx = args[0]; + let mut host_args = vec![vmctx]; + let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); + host_sig.params.push(ir::AbiParam::new(pointer_type)); + + host_sig.params.push(ir::AbiParam::new(ir::types::I8)); + host_args.push( + self.builder + .ins() + .iconst(ir::types::I8, if async_ { 1 } else { 0 }), + ); + + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + + // 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.task_yield()).unwrap(), + ); + let host_sig = self.builder.import_signature(host_sig); + let call = self + .builder + .ins() + .call_indirect(host_sig, host_fn, &host_args); + + let succeeded = self.builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(succeeded); + self.builder.ins().return_(&[]); + } + /// Loads a host function pointer for a libcall stored at the `offset` /// provided in the libcalls array. /// @@ -672,7 +1722,7 @@ impl<'a> TrampolineCompiler<'a> { self.raise_if_host_trapped(succeeded); } - fn raise_if_resource_trapped(&mut self, ret: ir::Value) -> ir::Value { + fn raise_if_i32_trapped(&mut self, ret: ir::Value) -> ir::Value { let minus_one = self.builder.ins().iconst(ir::types::I64, -1); let succeeded = self.builder.ins().icmp(IntCC::NotEqual, ret, minus_one); self.raise_if_host_trapped(succeeded); diff --git a/crates/environ/examples/factc.rs b/crates/environ/examples/factc.rs index 2c692a926465..d1f94586f4fe 100644 --- a/crates/environ/examples/factc.rs +++ b/crates/environ/examples/factc.rs @@ -147,6 +147,8 @@ impl Factc { realloc: Some(dummy_def()), // Lowering never allows `post-return` post_return: None, + async_: false, + callback: None, }, lift_options: AdapterOptions { instance: RuntimeComponentInstanceIndex::from_u32(1), @@ -159,6 +161,8 @@ impl Factc { } else { None }, + async_: false, + callback: None, }, func: dummy_def(), }); diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index b3899b4caa03..6988e4db7431 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -83,6 +83,10 @@ macro_rules! foreach_builtin_component_function { resource_enter_call(vmctx: vmctx); resource_exit_call(vmctx: vmctx) -> bool; + future_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64; + stream_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64; + error_context_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64; + trap(vmctx: vmctx, code: u8); utf8_to_utf8(src: ptr_u8, len: size, dst: ptr_u8) -> bool; diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index be5bc41f2052..d4b714ed7778 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -57,6 +57,9 @@ pub struct ComponentDfg { /// used by the host) pub reallocs: Intern, + /// Same as `reallocs`, but for async-lifted functions. + pub callbacks: Intern, + /// Same as `reallocs`, but for post-return. pub post_returns: Intern, @@ -103,7 +106,7 @@ pub struct ComponentDfg { /// The values here are the module that the adapter is present within along /// as the core wasm index of the export corresponding to the lowered /// version of the adapter. - pub adapter_paritionings: PrimaryMap, + pub adapter_partitionings: PrimaryMap, /// Defined resources in this component sorted by index with metadata about /// each resource. @@ -122,6 +125,16 @@ pub struct ComponentDfg { /// this component. pub num_resource_tables: usize, + /// The total number of future tables that will be used by this component. + pub num_future_tables: usize, + + /// The total number of stream tables that will be used by this component. + pub num_stream_tables: usize, + + /// The total number of error-context tables that will be used by this + /// component. + pub num_error_context_tables: usize, + /// An ordered list of side effects induced by instantiating this component. /// /// Currently all side effects are either instantiating core wasm modules or @@ -163,6 +176,7 @@ id! { pub struct InstanceId(u32); pub struct MemoryId(u32); pub struct ReallocId(u32); + pub struct CallbackId(u32); pub struct AdapterId(u32); pub struct PostReturnId(u32); pub struct AdapterModuleId(u32); @@ -211,7 +225,7 @@ pub enum CoreDef { /// This is a special variant not present in `info::CoreDef` which /// represents that this definition refers to a fused adapter function. This /// adapter is fully processed after the initial translation and - /// identificatino of adapters. + /// identification of adapters. /// /// During translation into `info::CoreDef` this variant is erased and /// replaced by `info::CoreDef::Export` since adapters are always @@ -269,10 +283,110 @@ pub enum Trampoline { ResourceNew(TypeResourceTableIndex), ResourceRep(TypeResourceTableIndex), ResourceDrop(TypeResourceTableIndex), + TaskBackpressure { + instance: RuntimeComponentInstanceIndex, + }, + TaskReturn, + TaskWait { + instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: MemoryId, + }, + TaskPoll { + instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: MemoryId, + }, + TaskYield { + async_: bool, + }, + SubtaskDrop { + instance: RuntimeComponentInstanceIndex, + }, + StreamNew { + ty: TypeStreamTableIndex, + }, + StreamRead { + ty: TypeStreamTableIndex, + options: CanonicalOptions, + }, + StreamWrite { + ty: TypeStreamTableIndex, + options: CanonicalOptions, + }, + StreamCancelRead { + ty: TypeStreamTableIndex, + async_: bool, + }, + StreamCancelWrite { + ty: TypeStreamTableIndex, + async_: bool, + }, + StreamCloseReadable { + ty: TypeStreamTableIndex, + }, + StreamCloseWritable { + ty: TypeStreamTableIndex, + }, + FutureNew { + ty: TypeFutureTableIndex, + }, + FutureRead { + ty: TypeFutureTableIndex, + options: CanonicalOptions, + }, + FutureWrite { + ty: TypeFutureTableIndex, + options: CanonicalOptions, + }, + FutureCancelRead { + ty: TypeFutureTableIndex, + async_: bool, + }, + FutureCancelWrite { + ty: TypeFutureTableIndex, + async_: bool, + }, + FutureCloseReadable { + ty: TypeFutureTableIndex, + }, + FutureCloseWritable { + ty: TypeFutureTableIndex, + }, + ErrorContextNew { + ty: TypeErrorContextTableIndex, + options: CanonicalOptions, + }, + ErrorContextDebugMessage { + ty: TypeErrorContextTableIndex, + options: CanonicalOptions, + }, + ErrorContextDrop { + ty: TypeErrorContextTableIndex, + }, ResourceTransferOwn, ResourceTransferBorrow, ResourceEnterCall, ResourceExitCall, + AsyncEnterCall, + AsyncExitCall(Option), + FutureTransfer, + StreamTransfer, + ErrorContextTransfer, +} + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +#[allow(missing_docs, reason = "self-describing fields")] +pub struct FutureInfo { + pub instance: RuntimeComponentInstanceIndex, + pub payload_type: Option, +} + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +#[allow(missing_docs, reason = "self-describing fields")] +pub struct StreamInfo { + pub instance: RuntimeComponentInstanceIndex, + pub payload_type: InterfaceType, } /// Same as `info::CanonicalOptions` @@ -283,7 +397,9 @@ pub struct CanonicalOptions { pub string_encoding: StringEncoding, pub memory: Option, pub realloc: Option, + pub callback: Option, pub post_return: Option, + pub async_: bool, } /// Same as `info::Resource` @@ -348,7 +464,7 @@ impl Default for Intern { impl ComponentDfg { /// Consumes the intermediate `ComponentDfg` to produce a final `Component` - /// with a linear innitializer list. + /// with a linear initializer list. pub fn finish( self, wasmtime_types: &mut ComponentTypesBuilder, @@ -360,6 +476,7 @@ impl ComponentDfg { runtime_memories: Default::default(), runtime_post_return: Default::default(), runtime_reallocs: Default::default(), + runtime_callbacks: Default::default(), runtime_instances: Default::default(), num_lowerings: 0, trampolines: Default::default(), @@ -400,11 +517,15 @@ impl ComponentDfg { num_runtime_memories: linearize.runtime_memories.len() as u32, num_runtime_post_returns: linearize.runtime_post_return.len() as u32, num_runtime_reallocs: linearize.runtime_reallocs.len() as u32, + num_runtime_callbacks: linearize.runtime_callbacks.len() as u32, num_runtime_instances: linearize.runtime_instances.len() as u32, imports: self.imports, import_types: self.import_types, num_runtime_component_instances: self.num_runtime_component_instances, num_resource_tables: self.num_resource_tables, + num_future_tables: self.num_future_tables, + num_stream_tables: self.num_stream_tables, + num_error_context_tables: self.num_error_context_tables, num_resources: (self.resources.len() + self.imported_resources.len()) as u32, imported_resources: self.imported_resources, defined_resource_instances: self @@ -431,6 +552,7 @@ struct LinearizeDfg<'a> { trampoline_map: HashMap, runtime_memories: HashMap, runtime_reallocs: HashMap, + runtime_callbacks: HashMap, runtime_post_return: HashMap, runtime_instances: HashMap, num_lowerings: u32, @@ -539,13 +661,16 @@ impl LinearizeDfg<'_> { fn options(&mut self, options: &CanonicalOptions) -> info::CanonicalOptions { let memory = options.memory.map(|mem| self.runtime_memory(mem)); let realloc = options.realloc.map(|mem| self.runtime_realloc(mem)); + 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 { instance: options.instance, string_encoding: options.string_encoding, memory, realloc, + callback, post_return, + async_: options.async_, } } @@ -567,6 +692,15 @@ impl LinearizeDfg<'_> { ) } + fn runtime_callback(&mut self, callback: CallbackId) -> RuntimeCallbackIndex { + self.intern( + callback, + |me| &mut me.runtime_callbacks, + |me, callback| me.core_def(&me.dfg.callbacks[callback]), + |index, def| GlobalInitializer::ExtractCallback(ExtractCallback { index, def }), + ) + } + fn runtime_post_return(&mut self, post_return: PostReturnId) -> RuntimePostReturnIndex { self.intern( post_return, @@ -625,10 +759,100 @@ impl LinearizeDfg<'_> { Trampoline::ResourceNew(ty) => info::Trampoline::ResourceNew(*ty), Trampoline::ResourceDrop(ty) => info::Trampoline::ResourceDrop(*ty), Trampoline::ResourceRep(ty) => info::Trampoline::ResourceRep(*ty), + Trampoline::TaskBackpressure { instance } => info::Trampoline::TaskBackpressure { + instance: *instance, + }, + Trampoline::TaskReturn => info::Trampoline::TaskReturn, + Trampoline::TaskWait { + instance, + async_, + memory, + } => info::Trampoline::TaskWait { + instance: *instance, + async_: *async_, + memory: self.runtime_memory(*memory), + }, + Trampoline::TaskPoll { + instance, + async_, + memory, + } => info::Trampoline::TaskPoll { + instance: *instance, + async_: *async_, + memory: self.runtime_memory(*memory), + }, + Trampoline::TaskYield { async_ } => info::Trampoline::TaskYield { async_: *async_ }, + Trampoline::SubtaskDrop { instance } => info::Trampoline::SubtaskDrop { + instance: *instance, + }, + Trampoline::StreamNew { ty } => info::Trampoline::StreamNew { ty: *ty }, + Trampoline::StreamRead { ty, options } => info::Trampoline::StreamRead { + ty: *ty, + options: self.options(options), + }, + Trampoline::StreamWrite { ty, options } => info::Trampoline::StreamWrite { + ty: *ty, + options: self.options(options), + }, + Trampoline::StreamCancelRead { ty, async_ } => info::Trampoline::StreamCancelRead { + ty: *ty, + async_: *async_, + }, + Trampoline::StreamCancelWrite { ty, async_ } => info::Trampoline::StreamCancelWrite { + ty: *ty, + async_: *async_, + }, + Trampoline::StreamCloseReadable { ty } => { + info::Trampoline::StreamCloseReadable { ty: *ty } + } + Trampoline::StreamCloseWritable { ty } => { + info::Trampoline::StreamCloseWritable { ty: *ty } + } + Trampoline::FutureNew { ty } => info::Trampoline::FutureNew { ty: *ty }, + Trampoline::FutureRead { ty, options } => info::Trampoline::FutureRead { + ty: *ty, + options: self.options(options), + }, + Trampoline::FutureWrite { ty, options } => info::Trampoline::FutureWrite { + ty: *ty, + options: self.options(options), + }, + Trampoline::FutureCancelRead { ty, async_ } => info::Trampoline::FutureCancelRead { + ty: *ty, + async_: *async_, + }, + Trampoline::FutureCancelWrite { ty, async_ } => info::Trampoline::FutureCancelWrite { + ty: *ty, + async_: *async_, + }, + Trampoline::FutureCloseReadable { ty } => { + info::Trampoline::FutureCloseReadable { ty: *ty } + } + Trampoline::FutureCloseWritable { ty } => { + info::Trampoline::FutureCloseWritable { ty: *ty } + } + Trampoline::ErrorContextNew { ty, options } => info::Trampoline::ErrorContextNew { + ty: *ty, + options: self.options(options), + }, + Trampoline::ErrorContextDebugMessage { ty, options } => { + info::Trampoline::ErrorContextDebugMessage { + ty: *ty, + options: self.options(options), + } + } + Trampoline::ErrorContextDrop { ty } => info::Trampoline::ErrorContextDrop { ty: *ty }, Trampoline::ResourceTransferOwn => info::Trampoline::ResourceTransferOwn, Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow, Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall, Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall, + Trampoline::AsyncEnterCall => info::Trampoline::AsyncEnterCall, + Trampoline::AsyncExitCall(callback) => { + info::Trampoline::AsyncExitCall(callback.map(|v| self.runtime_callback(v))) + } + Trampoline::FutureTransfer => info::Trampoline::FutureTransfer, + Trampoline::StreamTransfer => info::Trampoline::StreamTransfer, + Trampoline::ErrorContextTransfer => info::Trampoline::ErrorContextTransfer, }; let i1 = self.trampolines.push(*signature); let i2 = self.trampoline_defs.push(trampoline); @@ -650,7 +874,7 @@ impl LinearizeDfg<'_> { } fn adapter(&mut self, adapter: AdapterId) -> info::CoreExport { - let (adapter_module, entity_index) = self.dfg.adapter_paritionings[adapter]; + let (adapter_module, entity_index) = self.dfg.adapter_partitionings[adapter]; // Instantiates the adapter module if it hasn't already been // instantiated or otherwise returns the index that the module was diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index 8c5442a6d243..afb5f6098275 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -148,6 +148,10 @@ pub struct Component { /// `VMComponentContext`. pub num_runtime_reallocs: u32, + /// The number of runtime async callbacks (maximum `RuntimeCallbackIndex`) + /// needed to instantiate this component. + pub num_runtime_callbacks: u32, + /// Same as `num_runtime_reallocs`, but for post-return functions. pub num_runtime_post_returns: u32, @@ -158,7 +162,7 @@ pub struct Component { /// instantiate this component. pub num_lowerings: u32, - /// Maximal number of tables that required at runtime for resource-related + /// Maximal number of tables required at runtime for resource-related /// information in this component. pub num_resource_tables: usize, @@ -166,6 +170,18 @@ pub struct Component { /// component. pub num_resources: u32, + /// Maximal number of tables required at runtime for future-related + /// information in this component. + pub num_future_tables: usize, + + /// Maximal number of tables required at runtime for stream-related + /// information in this component. + pub num_stream_tables: usize, + + /// Maximal number of tables required at runtime for error-context-related + /// information in this component. + pub num_error_context_tables: usize, + /// Metadata about imported resources and where they are within the runtime /// imports array. /// @@ -252,6 +268,10 @@ pub enum GlobalInitializer { /// used as a `realloc` function. ExtractRealloc(ExtractRealloc), + /// Same as `ExtractMemory`, except it's extracting a function pointer to be + /// used as an async `callback` function. + ExtractCallback(ExtractCallback), + /// Same as `ExtractMemory`, except it's extracting a function pointer to be /// used as a `post-return` function. ExtractPostReturn(ExtractPostReturn), @@ -281,6 +301,15 @@ pub struct ExtractRealloc { pub def: CoreDef, } +/// Same as `ExtractMemory` but for the `callback` canonical option. +#[derive(Debug, Serialize, Deserialize)] +pub struct ExtractCallback { + /// The index of the callback being defined. + pub index: RuntimeCallbackIndex, + /// Where this callback is being extracted from. + pub def: CoreDef, +} + /// Same as `ExtractMemory` but for the `post-return` canonical option. #[derive(Debug, Serialize, Deserialize)] pub struct ExtractPostReturn { @@ -447,8 +476,14 @@ pub struct CanonicalOptions { /// The realloc function used by these options, if specified. pub realloc: Option, + /// The async callback function used by these options, if specified. + pub callback: Option, + /// The post-return function used by these options, if specified. pub post_return: Option, + + /// Whether to use the async ABI for lifting or lowering. + pub async_: bool, } /// Possible encodings of strings within the component model. @@ -644,6 +679,199 @@ pub enum Trampoline { /// Same as `ResourceNew`, but for the `resource.drop` intrinsic. ResourceDrop(TypeResourceTableIndex), + /// A `task.backpressure` intrinsic, which tells the host to enable or + /// disable backpressure for the caller's instance. + TaskBackpressure { + /// The specific component instance which is calling the intrinsic. + instance: RuntimeComponentInstanceIndex, + }, + + /// A `task.return` intrinsic, which returns a result to the caller of a + /// lifted export function. This allows the callee to continue executing + /// after returning a result. + TaskReturn, + + /// A `task.wait` intrinsic, which waits for at least one outstanding async + /// task/stream/future to make progress, returning the first such event. + TaskWait { + /// 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, + }, + + /// A `task.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. + TaskPoll { + /// 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, + }, + + /// A `task.yield` intrinsic, which yields control to the host so that other + /// tasks are able to make progress, if any. + TaskYield { + /// If `true`, indicates the caller instance maybe reentered. + async_: bool, + }, + + /// A `subtask.drop` intrinsic to drop a specified task which has completed. + SubtaskDrop { + /// The specific component instance which is calling the intrinsic. + instance: RuntimeComponentInstanceIndex, + }, + + /// A `stream.new` intrinsic to create a new `stream` handle of the + /// specified type. + StreamNew { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + }, + + /// A `stream.read` intrinsic to read from a `stream` of the specified type. + StreamRead { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + /// Any options (e.g. string encoding) to use when storing values to + /// memory. + options: CanonicalOptions, + }, + + /// A `stream.write` intrinsic to write to a `stream` of the specified type. + StreamWrite { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + /// Any options (e.g. string encoding) to use when storing values to + /// memory. + options: CanonicalOptions, + }, + + /// A `stream.cancel-read` intrinsic to cancel an in-progress read from a + /// `stream` of the specified type. + StreamCancelRead { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + /// If `false`, block until cancellation completes rather than return + /// `BLOCKED`. + async_: bool, + }, + + /// A `stream.cancel-write` intrinsic to cancel an in-progress write from a + /// `stream` of the specified type. + StreamCancelWrite { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + /// If `false`, block until cancellation completes rather than return + /// `BLOCKED`. + async_: bool, + }, + + /// A `stream.close-readable` intrinsic to close the readable end of a + /// `stream` of the specified type. + StreamCloseReadable { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + }, + + /// A `stream.close-writable` intrinsic to close the writable end of a + /// `stream` of the specified type. + StreamCloseWritable { + /// The table index for the specific `stream` type and caller instance. + ty: TypeStreamTableIndex, + }, + + /// A `future.new` intrinsic to create a new `future` handle of the + /// specified type. + FutureNew { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + }, + + /// A `future.read` intrinsic to read from a `future` of the specified type. + FutureRead { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + /// Any options (e.g. string encoding) to use when storing values to + /// memory. + options: CanonicalOptions, + }, + + /// A `future.write` intrinsic to write to a `future` of the specified type. + FutureWrite { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + /// Any options (e.g. string encoding) to use when storing values to + /// memory. + options: CanonicalOptions, + }, + + /// A `future.cancel-read` intrinsic to cancel an in-progress read from a + /// `future` of the specified type. + FutureCancelRead { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + /// If `false`, block until cancellation completes rather than return + /// `BLOCKED`. + async_: bool, + }, + + /// A `future.cancel-write` intrinsic to cancel an in-progress write from a + /// `future` of the specified type. + FutureCancelWrite { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + /// If `false`, block until cancellation completes rather than return + /// `BLOCKED`. + async_: bool, + }, + + /// A `future.close-readable` intrinsic to close the readable end of a + /// `future` of the specified type. + FutureCloseReadable { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + }, + + /// A `future.close-writable` intrinsic to close the writable end of a + /// `future` of the specified type. + FutureCloseWritable { + /// The table index for the specific `future` type and caller instance. + ty: TypeFutureTableIndex, + }, + + /// A `error-context.new` intrinsic to create a new `error-context` with a + /// specified debug message. + ErrorContextNew { + /// The table index for the `error-context` type in the caller instance. + ty: TypeErrorContextTableIndex, + /// String encoding, memory, etc. to use when loading debug message. + options: CanonicalOptions, + }, + + /// A `error-context.debug-message` intrinsic to get the debug message for a + /// specified `error-context`. + /// + /// Note that the debug message might not necessarily match what was passed + /// to `error.new`. + ErrorContextDebugMessage { + /// The table index for the `error-context` type in the caller instance. + ty: TypeErrorContextTableIndex, + /// String encoding, memory, etc. to use when storing debug message. + options: CanonicalOptions, + }, + + /// A `error-context.drop` intrinsic to drop a specified `error-context`. + ErrorContextDrop { + /// The table index for the `error-context` type in the caller instance. + ty: TypeErrorContextTableIndex, + }, + /// An intrinsic used by FACT-generated modules which will transfer an owned /// resource from one table to another. Used in component-to-component /// adapter trampolines. @@ -661,6 +889,43 @@ pub enum Trampoline { /// Same as `ResourceEnterCall` except for when exiting a call. ResourceExitCall, + + /// An intrinsic used by FACT-generated modules to begin a call to an + /// async-lowered import function. + AsyncEnterCall, + + /// An intrinsic used by FACT-generated modules to complete a call to an + /// async-lowered import function. + /// + /// Note that `AsyncEnterCall` and `AsyncExitCall` could theoretically be + /// combined into a single `AsyncCall` intrinsic, but we separate them to + /// allow the FACT-generated module to optionally call the callee directly + /// without an intermediate host stack frame. + AsyncExitCall(Option), + + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of a `future`. + /// + /// Transfering a `future` can either mean giving away the readable end + /// while retaining the writable end or only the former, depending on the + /// ownership status of the `future`. + FutureTransfer, + + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of a `stream`. + /// + /// Transfering a `stream` can either mean giving away the readable end + /// while retaining the writable end or only the former, depending on the + /// ownership status of the `stream`. + StreamTransfer, + + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of an `error-context`. + /// + /// Unlike futures, streams, and resource handles, `error-context` handles + /// are reference counted, meaning that sharing the handle with another + /// component does not invalidate the handle in the original component. + ErrorContextTransfer, } impl Trampoline { @@ -684,10 +949,38 @@ impl Trampoline { ResourceNew(i) => format!("component-resource-new[{}]", i.as_u32()), ResourceRep(i) => format!("component-resource-rep[{}]", i.as_u32()), ResourceDrop(i) => format!("component-resource-drop[{}]", i.as_u32()), + TaskBackpressure { .. } => format!("task-backpressure"), + TaskReturn => format!("task-return"), + TaskWait { .. } => format!("task-wait"), + TaskPoll { .. } => format!("task-poll"), + TaskYield { .. } => format!("task-yield"), + SubtaskDrop { .. } => format!("subtask-drop"), + StreamNew { .. } => format!("stream-new"), + StreamRead { .. } => format!("stream-read"), + StreamWrite { .. } => format!("stream-write"), + StreamCancelRead { .. } => format!("stream-cancel-read"), + StreamCancelWrite { .. } => format!("stream-cancel-write"), + StreamCloseReadable { .. } => format!("stream-close-readable"), + StreamCloseWritable { .. } => format!("stream-close-writable"), + FutureNew { .. } => format!("future-new"), + FutureRead { .. } => format!("future-read"), + FutureWrite { .. } => format!("future-write"), + FutureCancelRead { .. } => format!("future-cancel-read"), + FutureCancelWrite { .. } => format!("future-cancel-write"), + FutureCloseReadable { .. } => format!("future-close-readable"), + FutureCloseWritable { .. } => format!("future-close-writable"), + ErrorContextNew { .. } => format!("error-context-new"), + ErrorContextDebugMessage { .. } => format!("error-context-debug-message"), + ErrorContextDrop { .. } => format!("error-context-drop"), ResourceTransferOwn => format!("component-resource-transfer-own"), ResourceTransferBorrow => format!("component-resource-transfer-borrow"), ResourceEnterCall => format!("component-resource-enter-call"), ResourceExitCall => format!("component-resource-exit-call"), + AsyncEnterCall => format!("component-async-enter-call"), + AsyncExitCall(_) => format!("component-async-exit-call"), + FutureTransfer => format!("future-transfer"), + StreamTransfer => format!("stream-transfer"), + ErrorContextTransfer => format!("error-context-transfer"), } } } diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 23eb2306b56b..014a1511652a 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -12,8 +12,8 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::mem; use wasmparser::component_types::{ - AliasableResourceId, ComponentCoreModuleTypeId, ComponentEntityType, ComponentFuncTypeId, - ComponentInstanceTypeId, + AliasableResourceId, ComponentCoreModuleTypeId, ComponentDefinedTypeId, ComponentEntityType, + ComponentFuncTypeId, ComponentInstanceTypeId, }; use wasmparser::types::Types; use wasmparser::{Chunk, ComponentImportName, Encoding, Parser, Payload, Validator}; @@ -188,6 +188,105 @@ enum LocalInitializer<'data> { ResourceRep(AliasableResourceId, ModuleInternedTypeIndex), ResourceDrop(AliasableResourceId, ModuleInternedTypeIndex), + TaskBackpressure { + func: ModuleInternedTypeIndex, + }, + TaskReturn { + func: ModuleInternedTypeIndex, + }, + TaskWait { + func: ModuleInternedTypeIndex, + async_: bool, + memory: MemoryIndex, + }, + TaskPoll { + func: ModuleInternedTypeIndex, + async_: bool, + memory: MemoryIndex, + }, + TaskYield { + func: ModuleInternedTypeIndex, + async_: bool, + }, + SubtaskDrop { + func: ModuleInternedTypeIndex, + }, + StreamNew { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + StreamRead { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + StreamWrite { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + StreamCancelRead { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + async_: bool, + }, + StreamCancelWrite { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + async_: bool, + }, + StreamCloseReadable { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + StreamCloseWritable { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + FutureNew { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + FutureRead { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + FutureWrite { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + FutureCancelRead { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + async_: bool, + }, + FutureCancelWrite { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + async_: bool, + }, + FutureCloseReadable { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + FutureCloseWritable { + ty: ComponentDefinedTypeId, + func: ModuleInternedTypeIndex, + }, + ErrorContextNew { + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + ErrorContextDebugMessage { + func: ModuleInternedTypeIndex, + options: LocalCanonicalOptions, + }, + ErrorContextDrop { + func: ModuleInternedTypeIndex, + }, + // core wasm modules ModuleStatic(StaticModuleIndex, ComponentCoreModuleTypeId), @@ -255,6 +354,8 @@ struct LocalCanonicalOptions { memory: Option, realloc: Option, post_return: Option, + async_: bool, + callback: Option, } enum Action { @@ -471,7 +572,8 @@ impl<'a, 'data> Translator<'a, 'data> { // Entries in the canonical section will get initializers recorded // with the listed options for lifting/lowering. Payload::ComponentCanonicalSection(s) => { - let mut core_func_index = self.validator.types(0).unwrap().function_count(); + let types = self.validator.types(0).unwrap(); + let mut core_func_index = types.function_count(); self.validator.component_canonical_section(&s)?; for func in s { let types = self.validator.types(0).unwrap(); @@ -521,11 +623,153 @@ impl<'a, 'data> Translator<'a, 'data> { core_func_index += 1; LocalInitializer::ResourceRep(resource, ty) } - wasmparser::CanonicalFunction::ThreadSpawn { .. } | wasmparser::CanonicalFunction::ThreadHwConcurrency => { bail!("unsupported intrinsic") } + wasmparser::CanonicalFunction::TaskBackpressure => { + let core_type = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::TaskBackpressure { func: core_type } + } + wasmparser::CanonicalFunction::TaskReturn { .. } => { + let core_type = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::TaskReturn { func: core_type } + } + wasmparser::CanonicalFunction::TaskWait { async_, memory } => { + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::TaskWait { + func, + async_, + memory: MemoryIndex::from_u32(memory), + } + } + wasmparser::CanonicalFunction::TaskPoll { async_, memory } => { + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::TaskPoll { + func, + async_, + memory: MemoryIndex::from_u32(memory), + } + } + wasmparser::CanonicalFunction::TaskYield { async_ } => { + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::TaskYield { func, async_ } + } + wasmparser::CanonicalFunction::SubtaskDrop => { + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::SubtaskDrop { func } + } + wasmparser::CanonicalFunction::StreamNew { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamNew { ty, func } + } + wasmparser::CanonicalFunction::StreamRead { ty, options } => { + let ty = types.component_defined_type_at(ty); + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamRead { ty, func, options } + } + wasmparser::CanonicalFunction::StreamWrite { ty, options } => { + let ty = types.component_defined_type_at(ty); + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamWrite { ty, func, options } + } + wasmparser::CanonicalFunction::StreamCancelRead { ty, async_ } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamCancelRead { ty, func, async_ } + } + wasmparser::CanonicalFunction::StreamCancelWrite { ty, async_ } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamCancelWrite { ty, func, async_ } + } + wasmparser::CanonicalFunction::StreamCloseReadable { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamCloseReadable { ty, func } + } + wasmparser::CanonicalFunction::StreamCloseWritable { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::StreamCloseWritable { ty, func } + } + wasmparser::CanonicalFunction::FutureNew { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureNew { ty, func } + } + wasmparser::CanonicalFunction::FutureRead { ty, options } => { + let ty = types.component_defined_type_at(ty); + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureRead { ty, func, options } + } + wasmparser::CanonicalFunction::FutureWrite { ty, options } => { + let ty = types.component_defined_type_at(ty); + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureWrite { ty, func, options } + } + wasmparser::CanonicalFunction::FutureCancelRead { ty, async_ } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureCancelRead { ty, func, async_ } + } + wasmparser::CanonicalFunction::FutureCancelWrite { ty, async_ } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureCancelWrite { ty, func, async_ } + } + wasmparser::CanonicalFunction::FutureCloseReadable { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureCloseReadable { ty, func } + } + wasmparser::CanonicalFunction::FutureCloseWritable { ty } => { + let ty = types.component_defined_type_at(ty); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::FutureCloseWritable { ty, func } + } + wasmparser::CanonicalFunction::ErrorContextNew { options } => { + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::ErrorContextNew { func, options } + } + wasmparser::CanonicalFunction::ErrorContextDebugMessage { options } => { + let options = self.canonical_options(&options); + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::ErrorContextDebugMessage { func, options } + } + wasmparser::CanonicalFunction::ErrorContextDrop => { + let func = self.core_func_signature(core_func_index)?; + core_func_index += 1; + LocalInitializer::ErrorContextDrop { func } + } }; self.result.initializers.push(init); } @@ -896,6 +1140,8 @@ impl<'a, 'data> Translator<'a, 'data> { memory: None, realloc: None, post_return: None, + async_: false, + callback: None, }; for opt in opts { match opt { @@ -920,6 +1166,11 @@ impl<'a, 'data> Translator<'a, 'data> { let idx = FuncIndex::from_u32(*idx); ret.post_return = Some(idx); } + wasmparser::CanonicalOption::Async => ret.async_ = true, + wasmparser::CanonicalOption::Callback(idx) => { + let idx = FuncIndex::from_u32(*idx); + ret.callback = Some(idx); + } } } return ret; diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 8989e6fce1a1..6f28c82198a5 100644 --- a/crates/environ/src/component/translate/adapt.rs +++ b/crates/environ/src/component/translate/adapt.rs @@ -157,8 +157,12 @@ pub struct AdapterOptions { pub memory64: bool, /// An optional definition of `realloc` to used. pub realloc: Option, + /// The async callback function used by these options, if specified. + pub callback: Option, /// An optional definition of a `post-return` to use. pub post_return: Option, + /// Whether to use the async ABI for lifting or lowering. + pub async_: bool, } impl<'data> Translator<'_, 'data> { @@ -192,6 +196,8 @@ impl<'data> Translator<'_, 'data> { names.push(name); } let wasm = module.encode(); + std::fs::write("/tmp/adapter.wasm", &wasm).unwrap(); + wasmparser::Validator::new().validate_all(&wasm).unwrap(); let imports = module.imports().to_vec(); // Extend the lifetime of the owned `wasm: Vec` on the stack to @@ -227,7 +233,7 @@ impl<'data> Translator<'_, 'data> { // in-order here as well. (with an assert to double-check) for (adapter, name) in adapter_module.adapters.iter().zip(&names) { let index = translation.module.exports[name]; - let i = component.adapter_paritionings.push((module_id, index)); + let i = component.adapter_partitionings.push((module_id, index)); assert_eq!(i, *adapter); } @@ -300,6 +306,15 @@ fn fact_import_to_core_def( } fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall), fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall), + fact::Import::AsyncEnterCall => simple_intrinsic(dfg::Trampoline::AsyncEnterCall), + fact::Import::AsyncExitCall(callback) => simple_intrinsic(dfg::Trampoline::AsyncExitCall( + callback.clone().map(|v| dfg.callbacks.push(v)), + )), + fact::Import::FutureTransfer => simple_intrinsic(dfg::Trampoline::FutureTransfer), + fact::Import::StreamTransfer => simple_intrinsic(dfg::Trampoline::StreamTransfer), + fact::Import::ErrorContextTransfer => { + simple_intrinsic(dfg::Trampoline::ErrorContextTransfer) + } } } @@ -363,6 +378,9 @@ impl PartitionAdapterModules { if let Some(def) = &options.realloc { self.core_def(dfg, def); } + if let Some(def) = &options.callback { + self.core_def(dfg, def); + } if let Some(def) = &options.post_return { self.core_def(dfg, def); } diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index 881cb7440f08..ba16cad9b70f 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -135,6 +135,9 @@ pub(super) fn run( } inliner.result.exports = export_map; inliner.result.num_resource_tables = types.num_resource_tables(); + inliner.result.num_future_tables = types.num_future_tables(); + inliner.result.num_stream_tables = types.num_stream_tables(); + inliner.result.num_error_context_tables = types.num_error_context_tables(); Ok(inliner.result) } @@ -667,6 +670,288 @@ impl<'a> Inliner<'a> { .push((*ty, dfg::Trampoline::ResourceDrop(id))); frame.funcs.push(dfg::CoreDef::Trampoline(index)); } + TaskBackpressure { func } => { + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::TaskBackpressure { + instance: frame.instance, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + TaskReturn { func } => { + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::TaskReturn)); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + TaskWait { + 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::TaskWait { + instance: frame.instance, + async_: *async_, + memory, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + TaskPoll { + 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::TaskPoll { + instance: frame.instance, + async_: *async_, + memory, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + TaskYield { func, async_ } => { + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::TaskYield { async_: *async_ })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + SubtaskDrop { func } => { + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::SubtaskDrop { + instance: frame.instance, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamNew { ty, func } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::StreamNew { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamRead { ty, func, options } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::StreamRead { ty, options })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamWrite { ty, func, options } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::StreamWrite { ty, options })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamCancelRead { ty, func, async_ } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::StreamCancelRead { + ty, + async_: *async_, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamCancelWrite { ty, func, async_ } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::StreamCancelWrite { + ty, + async_: *async_, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamCloseReadable { ty, func } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::StreamCloseReadable { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + StreamCloseWritable { ty, func } => { + let InterfaceType::Stream(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::StreamCloseWritable { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureNew { ty, func } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::FutureNew { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureRead { ty, func, options } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::FutureRead { ty, options })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureWrite { ty, func, options } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::FutureWrite { ty, options })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureCancelRead { ty, func, async_ } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::FutureCancelRead { + ty, + async_: *async_, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureCancelWrite { ty, func, async_ } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::FutureCancelWrite { + ty, + async_: *async_, + }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureCloseReadable { ty, func } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::FutureCloseReadable { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + FutureCloseWritable { ty, func } => { + let InterfaceType::Future(ty) = + types.defined_type(frame.translation.types_ref(), *ty)? + else { + unreachable!() + }; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::FutureCloseWritable { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + ErrorContextNew { func, options } => { + let ty = types.error_context_type()?; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::ErrorContextNew { ty, options })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + ErrorContextDebugMessage { func, options } => { + let ty = types.error_context_type()?; + let options = self.adapter_options(frame, types, options); + let options = self.canonical_options(options); + let index = self.result.trampolines.push(( + *func, + dfg::Trampoline::ErrorContextDebugMessage { ty, options }, + )); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } + ErrorContextDrop { func } => { + let ty = types.error_context_type()?; + let index = self + .result + .trampolines + .push((*func, dfg::Trampoline::ErrorContextDrop { ty })); + frame.funcs.push(dfg::CoreDef::Trampoline(index)); + } ModuleStatic(idx, ty) => { frame.modules.push(ModuleDef::Static(*idx, *ty)); @@ -948,6 +1233,41 @@ impl<'a> Inliner<'a> { } } + fn memory( + &mut self, + frame: &InlinerFrame<'a>, + types: &ComponentTypesBuilder, + memory: MemoryIndex, + ) -> (dfg::CoreExport, bool) { + let memory = frame.memories[memory].clone().map_index(|i| match i { + EntityIndex::Memory(i) => i, + _ => unreachable!(), + }); + let memory64 = match &self.runtime_instances[memory.instance] { + InstanceModule::Static(idx) => match &memory.item { + ExportItem::Index(i) => { + let ty = &self.nested_modules[*idx].module.memories[*i]; + match ty.idx_type { + IndexType::I32 => false, + IndexType::I64 => true, + } + } + ExportItem::Name(_) => unreachable!(), + }, + InstanceModule::Import(ty) => match &memory.item { + ExportItem::Name(name) => match types[*ty].exports[name] { + EntityType::Memory(m) => match m.idx_type { + IndexType::I32 => false, + IndexType::I64 => true, + }, + _ => unreachable!(), + }, + ExportItem::Index(_) => unreachable!(), + }, + }; + (memory, memory64) + } + /// Translates a `LocalCanonicalOptions` which indexes into the `frame` /// specified into a runtime representation. fn adapter_options( @@ -988,6 +1308,7 @@ impl<'a> Inliner<'a> { None => false, }; let realloc = options.realloc.map(|i| frame.funcs[i].clone()); + let callback = options.callback.map(|i| frame.funcs[i].clone()); let post_return = options.post_return.map(|i| frame.funcs[i].clone()); AdapterOptions { instance: frame.instance, @@ -995,7 +1316,9 @@ impl<'a> Inliner<'a> { memory, memory64, realloc, + callback, post_return, + async_: options.async_, } } @@ -1008,6 +1331,7 @@ impl<'a> Inliner<'a> { .memory .map(|export| self.result.memories.push(export)); let realloc = options.realloc.map(|def| self.result.reallocs.push(def)); + let callback = options.callback.map(|def| self.result.callbacks.push(def)); let post_return = options .post_return .map(|def| self.result.post_returns.push(def)); @@ -1016,7 +1340,9 @@ impl<'a> Inliner<'a> { string_encoding: options.string_encoding, memory, realloc, + callback, post_return, + async_: options.async_, } } diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index de3896c18f04..7516c4425963 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -89,6 +89,28 @@ indices! { pub struct TypeResultIndex(u32); /// Index pointing to a list type in the component model. pub struct TypeListIndex(u32); + /// Index pointing to a future type in the component model. + pub struct TypeFutureIndex(u32); + /// Index pointing to a future table within a component. + /// + /// This is analogous to `TypeResourceTableIndex` in that it tracks + /// ownership of futures within each (sub)component instance. + pub struct TypeFutureTableIndex(u32); + /// Index pointing to a stream type in the component model. + pub struct TypeStreamIndex(u32); + /// Index pointing to a stream table within a component. + /// + /// This is analogous to `TypeResourceTableIndex` in that it tracks + /// ownership of stream within each (sub)component instance. + pub struct TypeStreamTableIndex(u32); + /// Index pointing to a error context table within a component. + /// + /// This is analogous to `TypeResourceTableIndex` in that it tracks + /// ownership of error contexts within each (sub)component instance. + pub struct TypeErrorContextTableIndex(u32); + + /// Index pointing to an interned `task.return` type within a component. + pub struct TypeTaskReturnIndex(u32); /// Index pointing to a resource table within a component. /// @@ -186,6 +208,9 @@ indices! { /// Same as `RuntimeMemoryIndex` except for the `realloc` function. pub struct RuntimeReallocIndex(u32); + /// Same as `RuntimeMemoryIndex` except for the `callback` function. + pub struct RuntimeCallbackIndex(u32); + /// Same as `RuntimeMemoryIndex` except for the `post-return` function. pub struct RuntimePostReturnIndex(u32); @@ -194,7 +219,7 @@ indices! { /// /// This is used to point to various bits of metadata within a compiled /// component and is stored in the final compilation artifact. This does not - /// have a direct corresponance to any wasm definition. + /// have a direct correspondence to any wasm definition. pub struct TrampolineIndex(u32); /// An index into `Component::export_items` at the end of compilation. @@ -237,8 +262,13 @@ pub struct ComponentTypes { pub(super) options: PrimaryMap, pub(super) results: PrimaryMap, pub(super) resource_tables: PrimaryMap, - pub(super) module_types: Option, + pub(super) futures: PrimaryMap, + pub(super) future_tables: PrimaryMap, + pub(super) streams: PrimaryMap, + pub(super) stream_tables: PrimaryMap, + pub(super) error_context_tables: PrimaryMap, + pub(super) task_returns: PrimaryMap, } impl ComponentTypes { @@ -261,7 +291,10 @@ impl ComponentTypes { | InterfaceType::Float32 | InterfaceType::Char | InterfaceType::Own(_) - | InterfaceType::Borrow(_) => &CanonicalAbiInfo::SCALAR4, + | InterfaceType::Borrow(_) + | InterfaceType::Future(_) + | InterfaceType::Stream(_) + | InterfaceType::ErrorContext(_) => &CanonicalAbiInfo::SCALAR4, InterfaceType::U64 | InterfaceType::S64 | InterfaceType::Float64 => { &CanonicalAbiInfo::SCALAR8 @@ -320,6 +353,11 @@ impl_index! { impl Index for ComponentTypes { TypeResult => results } impl Index for ComponentTypes { TypeList => lists } impl Index for ComponentTypes { TypeResourceTable => resource_tables } + impl Index for ComponentTypes { TypeFuture => futures } + impl Index for ComponentTypes { TypeStream => streams } + impl Index for ComponentTypes { TypeFutureTable => future_tables } + impl Index for ComponentTypes { TypeStreamTable => stream_tables } + impl Index for ComponentTypes { TypeErrorContextTable => error_context_tables } } // Additionally forward anything that can index `ModuleTypes` to `ModuleTypes` @@ -430,6 +468,20 @@ pub struct TypeFunc { pub params: TypeTupleIndex, /// Results of the function represented as a tuple. pub results: TypeTupleIndex, + /// Expected core func type for memory32 `task.return` calls for this function. + pub task_return_type32: TypeTaskReturnIndex, + /// Expected core func type for memory64 `task.return` calls for this function. + pub task_return_type64: TypeTaskReturnIndex, +} + +/// A core type representing the expected `task.return` signature for a +/// component function. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeTaskReturn { + /// Core type parameters for the signature. + /// + /// Note that `task.return` never returns results. + pub params: Vec, } /// All possible interface types that values can have. @@ -464,6 +516,9 @@ pub enum InterfaceType { Result(TypeResultIndex), Own(TypeResourceTableIndex), Borrow(TypeResourceTableIndex), + Future(TypeFutureTableIndex), + Stream(TypeStreamTableIndex), + ErrorContext(TypeErrorContextTableIndex), } impl From<&wasmparser::PrimitiveValType> for InterfaceType { @@ -972,6 +1027,45 @@ pub struct TypeResult { pub info: VariantInfo, } +/// Shape of a "future" interface type. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeFuture { + /// The `T` in `future` + pub payload: Option, +} + +/// Metadata about a future table added to a component. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeFutureTable { + /// The specific future type this table is used for. + pub ty: TypeFutureIndex, + /// The specific component instance this table is used for. + pub instance: RuntimeComponentInstanceIndex, +} + +/// Shape of a "stream" interface type. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeStream { + /// The `T` in `stream` + pub payload: InterfaceType, +} + +/// Metadata about a stream table added to a component. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeStreamTable { + /// The specific stream type this table is used for. + pub ty: TypeStreamIndex, + /// The specific component instance this table is used for. + pub instance: RuntimeComponentInstanceIndex, +} + +/// Metadata about a error context table added to a component. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeErrorContextTable { + /// The specific component instance this table is used for. + pub instance: RuntimeComponentInstanceIndex, +} + /// Metadata about a resource table added to a component. #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] pub struct TypeResourceTable { @@ -1049,7 +1143,7 @@ impl FlatTypes<'_> { // Note that this is intentionally duplicated here to keep the size to 1 byte // regardless to changes in the core wasm type system since this will only // ever use integers/floats for the foreseeable future. -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Eq, Copy, Clone)] #[allow(missing_docs, reason = "self-describing variants")] pub enum FlatType { I32, diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs index 8defc7ac9ad4..1bc47c785584 100644 --- a/crates/environ/src/component/types_builder.rs +++ b/crates/environ/src/component/types_builder.rs @@ -31,7 +31,7 @@ pub use resources::ResourcesBuilder; /// Some more information about this can be found in #4814 const MAX_TYPE_DEPTH: u32 = 100; -/// Structured used to build a [`ComponentTypes`] during translation. +/// Structure used to build a [`ComponentTypes`] during translation. /// /// This contains tables to intern any component types found as well as /// managing building up core wasm [`ModuleTypes`] as well. @@ -45,6 +45,12 @@ pub struct ComponentTypesBuilder { flags: HashMap, options: HashMap, results: HashMap, + futures: HashMap, + streams: HashMap, + future_tables: HashMap, + stream_tables: HashMap, + error_context_tables: HashMap, + task_returns: HashMap, component_types: ComponentTypes, module_types: ModuleTypesBuilder, @@ -70,15 +76,16 @@ where macro_rules! intern_and_fill_flat_types { ($me:ident, $name:ident, $val:ident) => {{ if let Some(idx) = $me.$name.get(&$val) { - return *idx; + *idx + } else { + let idx = $me.component_types.$name.push($val.clone()); + let mut info = TypeInformation::new(); + info.$name($me, &$val); + let idx2 = $me.type_info.$name.push(info); + assert_eq!(idx, idx2); + $me.$name.insert($val, idx); + idx } - let idx = $me.component_types.$name.push($val.clone()); - let mut info = TypeInformation::new(); - info.$name($me, &$val); - let idx2 = $me.type_info.$name.push(info); - assert_eq!(idx, idx2); - $me.$name.insert($val, idx); - return idx; }}; } @@ -97,6 +104,12 @@ impl ComponentTypesBuilder { flags: HashMap::default(), options: HashMap::default(), results: HashMap::default(), + futures: HashMap::default(), + streams: HashMap::default(), + future_tables: HashMap::default(), + stream_tables: HashMap::default(), + error_context_tables: HashMap::default(), + task_returns: HashMap::default(), component_types: ComponentTypes::default(), type_info: TypeInformationCache::default(), resources: ResourcesBuilder::default(), @@ -184,6 +197,24 @@ impl ComponentTypesBuilder { self.component_types.resource_tables.len() } + /// Returns the number of future tables allocated so far, or the maximum + /// `TypeFutureTableIndex`. + pub fn num_future_tables(&self) -> usize { + self.component_types.future_tables.len() + } + + /// Returns the number of stream tables allocated so far, or the maximum + /// `TypeStreamTableIndex`. + pub fn num_stream_tables(&self) -> usize { + self.component_types.stream_tables.len() + } + + /// Returns the number of error-context tables allocated so far, or the maximum + /// `TypeErrorContextTableIndex`. + pub fn num_error_context_tables(&self) -> usize { + self.component_types.error_context_tables.len() + } + /// Returns a mutable reference to the underlying `ResourcesBuilder`. pub fn resources_mut(&mut self) -> &mut ResourcesBuilder { &mut self.resources @@ -215,10 +246,24 @@ impl ComponentTypesBuilder { .iter() .map(|(_name, ty)| self.valtype(types, ty)) .collect::>()?; + let params = self.new_tuple_type(params); + let results = self.new_tuple_type(results); + let (task_return_type32, task_return_type64) = + if let Some(types) = self.flat_types(&InterfaceType::Tuple(results)) { + (types.memory32.to_vec(), types.memory64.to_vec()) + } else { + (vec![FlatType::I32], vec![FlatType::I64]) + }; let ty = TypeFunc { param_names, - params: self.new_tuple_type(params), - results: self.new_tuple_type(results), + params, + results, + task_return_type32: self.add_task_return_type(TypeTaskReturn { + params: task_return_type32, + }), + task_return_type64: self.add_task_return_type(TypeTaskReturn { + params: task_return_type64, + }), }; Ok(self.add_func_type(ty)) } @@ -356,7 +401,8 @@ impl ComponentTypesBuilder { }) } - fn defined_type( + /// Convert a wasmparser `ComponentDefinedTypeId` into Wasmtime's type representation. + pub fn defined_type( &mut self, types: TypesRef<'_>, id: ComponentDefinedTypeId, @@ -380,6 +426,15 @@ impl ComponentTypesBuilder { ComponentDefinedType::Borrow(r) => { InterfaceType::Borrow(self.resource_id(r.resource())) } + ComponentDefinedType::Future(ty) => { + InterfaceType::Future(self.future_table_type(types, ty)?) + } + ComponentDefinedType::Stream(ty) => { + InterfaceType::Stream(self.stream_table_type(types, ty)?) + } + ComponentDefinedType::ErrorContext => { + InterfaceType::ErrorContext(self.error_context_table_type()?) + } }; let info = self.type_information(&ret); if info.depth > MAX_TYPE_DEPTH { @@ -388,6 +443,11 @@ impl ComponentTypesBuilder { Ok(ret) } + /// Retrieve Wasmtime's type representation of the `error-context` type. + pub fn error_context_type(&mut self) -> Result { + self.error_context_table_type() + } + fn valtype(&mut self, types: TypesRef<'_>, ty: &ComponentValType) -> Result { assert_eq!(types.id(), self.module_types.validator_id()); match ty { @@ -513,6 +573,38 @@ impl ComponentTypesBuilder { Ok(self.add_result_type(TypeResult { ok, err, abi, info })) } + fn future_table_type( + &mut self, + types: TypesRef<'_>, + ty: &Option, + ) -> Result { + let payload = ty.as_ref().map(|ty| self.valtype(types, ty)).transpose()?; + let ty = self.add_future_type(TypeFuture { payload }); + Ok(self.add_future_table_type(TypeFutureTable { + ty, + instance: self.resources.get_current_instance().unwrap(), + })) + } + + fn stream_table_type( + &mut self, + types: TypesRef<'_>, + ty: &ComponentValType, + ) -> Result { + let payload = self.valtype(types, ty)?; + let ty = self.add_stream_type(TypeStream { payload }); + Ok(self.add_stream_table_type(TypeStreamTable { + ty, + instance: self.resources.get_current_instance().unwrap(), + })) + } + + fn error_context_table_type(&mut self) -> Result { + Ok(self.add_error_context_table_type(TypeErrorContextTable { + instance: self.resources.get_current_instance().unwrap(), + })) + } + fn list_type(&mut self, types: TypesRef<'_>, ty: &ComponentValType) -> Result { assert_eq!(types.id(), self.module_types.validator_id()); let element = self.valtype(types, ty)?; @@ -565,11 +657,66 @@ impl ComponentTypesBuilder { intern_and_fill_flat_types!(self, results, ty) } - /// Interns a new type within this type information. + /// Interns a new list type within this type information. pub fn add_list_type(&mut self, ty: TypeList) -> TypeListIndex { intern_and_fill_flat_types!(self, lists, ty) } + /// Interns a new future type within this type information. + pub fn add_future_type(&mut self, ty: TypeFuture) -> TypeFutureIndex { + intern(&mut self.futures, &mut self.component_types.futures, ty) + } + + /// Interns a new future table type within this type information. + pub fn add_future_table_type(&mut self, ty: TypeFutureTable) -> TypeFutureTableIndex { + intern( + &mut self.future_tables, + &mut self.component_types.future_tables, + ty, + ) + } + + /// Interns a new stream type within this type information. + pub fn add_stream_type(&mut self, ty: TypeStream) -> TypeStreamIndex { + intern(&mut self.streams, &mut self.component_types.streams, ty) + } + + /// Interns a new stream table type within this type information. + pub fn add_stream_table_type(&mut self, ty: TypeStreamTable) -> TypeStreamTableIndex { + intern( + &mut self.stream_tables, + &mut self.component_types.stream_tables, + ty, + ) + } + + /// Interns a new error context table type within this type information. + pub fn add_error_context_table_type( + &mut self, + ty: TypeErrorContextTable, + ) -> TypeErrorContextTableIndex { + intern( + &mut self.error_context_tables, + &mut self.component_types.error_context_tables, + ty, + ) + } + + /// Interns a new task return type within this type information. + pub fn add_task_return_type(&mut self, ty: TypeTaskReturn) -> TypeTaskReturnIndex { + intern( + &mut self.task_returns, + &mut self.component_types.task_returns, + ty, + ) + } + + /// Gets a previously interned task return type within this type + /// information, if any. + pub fn get_task_return_type(&self, ty: &TypeTaskReturn) -> Option { + self.task_returns.get(ty).copied() + } + /// Returns the canonical ABI information about the specified type. pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo { self.component_types.canonical_abi(ty) @@ -600,7 +747,10 @@ impl ComponentTypesBuilder { | InterfaceType::U32 | InterfaceType::S32 | InterfaceType::Char - | InterfaceType::Own(_) => { + | InterfaceType::Own(_) + | InterfaceType::Future(_) + | InterfaceType::Stream(_) + | InterfaceType::ErrorContext(_) => { static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32); &INFO } diff --git a/crates/environ/src/component/types_builder/resources.rs b/crates/environ/src/component/types_builder/resources.rs index d02426d49b6b..cba01c60747f 100644 --- a/crates/environ/src/component/types_builder/resources.rs +++ b/crates/environ/src/component/types_builder/resources.rs @@ -231,4 +231,9 @@ impl ResourcesBuilder { pub fn set_current_instance(&mut self, instance: RuntimeComponentInstanceIndex) { self.current_instance = Some(instance); } + + /// Retrieves the `current_instance` field. + pub fn get_current_instance(&self) -> Option { + self.current_instance + } } diff --git a/crates/environ/src/component/vmcomponent_offsets.rs b/crates/environ/src/component/vmcomponent_offsets.rs index a46b855461da..ca277c3b19d2 100644 --- a/crates/environ/src/component/vmcomponent_offsets.rs +++ b/crates/environ/src/component/vmcomponent_offsets.rs @@ -4,13 +4,40 @@ // magic: u32, // builtins: &'static VMComponentBuiltins, // store: *mut dyn Store, +// task_backpressure: VMTaskBackpressureCallback, +// task_return: VMTaskReturnCallback, +// task_wait: VMTaskWaitOrPollCallback, +// task_poll: VMTaskWaitOrPollCallback, +// task_yield: VMTaskYieldCallback, +// subtask_drop: VMSubtaskDropCallback, +// async_enter: VMAsyncEnterCallback, +// async_exit: VMAsyncExitCallback, +// future_new: VMFutureNewCallback, +// future_write: VMFutureTransmitCallback, +// future_read: VMFutureTransmitCallback, +// future_cancel_write: VMFutureCancelCallback, +// future_cancel_read: VMFutureCancelCallback, +// stream_cancel_write: VMStreamCancelCallback, +// stream_cancel_read: VMStreamCancelCallback, +// future_close_writable: VMFutureCloseWritableCallback, +// future_close_readable: VMFutureCloseReadableCallback, +// stream_close_writable: VMStreamCloseWritableCallback, +// stream_close_readable: VMStreamCloseReadableCallback, +// stream_new: VMStreamNewCallback, +// stream_write: VMStreamTransmitCallback, +// stream_read: VMStreamTransmitCallback, +// flat_stream_write: VMFlatStreamTransmitCallback, +// flat_stream_read: VMFlatStreamTransmitCallback, +// error_context_new: VMErrorContextNewCallback, +// error_context_debug_string: VMErrorContextDebugStringCallback, +// error_context_drop: VMErrorContextDropCallback, // limits: *const VMRuntimeLimits, // flags: [VMGlobalDefinition; component.num_runtime_component_instances], // trampoline_func_refs: [VMFuncRef; component.num_trampolines], // lowerings: [VMLowering; component.num_lowerings], -// memories: [*mut VMMemoryDefinition; component.num_memories], -// reallocs: [*mut VMFuncRef; component.num_reallocs], -// post_returns: [*mut VMFuncRef; component.num_post_returns], +// memories: [*mut VMMemoryDefinition; component.num_runtime_memories], +// reallocs: [*mut VMFuncRef; component.num_runtime_reallocs], +// post_returns: [*mut VMFuncRef; component.num_runtime_post_returns], // resource_destructors: [*mut VMFuncRef; component.num_resources], // } @@ -47,6 +74,8 @@ pub struct VMComponentOffsets

{ pub num_runtime_memories: u32, /// The number of reallocs which are recorded in this component for options. pub num_runtime_reallocs: u32, + /// The number of callbacks which are recorded in this component for options. + pub num_runtime_callbacks: u32, /// The number of post-returns which are recorded in this component for options. pub num_runtime_post_returns: u32, /// Number of component instances internally in the component (always at @@ -61,12 +90,40 @@ pub struct VMComponentOffsets

{ magic: u32, builtins: u32, store: u32, + task_backpressure: u32, + task_return: u32, + task_wait: u32, + task_poll: u32, + task_yield: u32, + subtask_drop: u32, + async_enter: u32, + async_exit: u32, + future_new: u32, + future_write: u32, + future_read: u32, + future_cancel_write: u32, + future_cancel_read: u32, + future_close_writable: u32, + future_close_readable: u32, + stream_new: u32, + stream_write: u32, + stream_read: u32, + stream_cancel_write: u32, + stream_cancel_read: u32, + stream_close_writable: u32, + stream_close_readable: u32, + flat_stream_write: u32, + flat_stream_read: u32, + error_context_new: u32, + error_context_debug_message: u32, + error_context_drop: u32, limits: u32, flags: u32, trampoline_func_refs: u32, lowerings: u32, memories: u32, reallocs: u32, + callbacks: u32, post_returns: u32, resource_destructors: u32, size: u32, @@ -87,6 +144,7 @@ impl VMComponentOffsets

{ num_lowerings: component.num_lowerings, num_runtime_memories: component.num_runtime_memories.try_into().unwrap(), num_runtime_reallocs: component.num_runtime_reallocs.try_into().unwrap(), + num_runtime_callbacks: component.num_runtime_callbacks.try_into().unwrap(), num_runtime_post_returns: component.num_runtime_post_returns.try_into().unwrap(), num_runtime_component_instances: component .num_runtime_component_instances @@ -103,9 +161,37 @@ impl VMComponentOffsets

{ lowerings: 0, memories: 0, reallocs: 0, + callbacks: 0, post_returns: 0, resource_destructors: 0, size: 0, + task_backpressure: 0, + task_return: 0, + task_wait: 0, + task_poll: 0, + task_yield: 0, + subtask_drop: 0, + async_enter: 0, + async_exit: 0, + future_new: 0, + future_write: 0, + future_read: 0, + future_cancel_write: 0, + future_cancel_read: 0, + future_close_writable: 0, + future_close_readable: 0, + stream_new: 0, + stream_write: 0, + stream_read: 0, + stream_cancel_write: 0, + stream_cancel_read: 0, + stream_close_writable: 0, + stream_close_readable: 0, + flat_stream_write: 0, + flat_stream_read: 0, + error_context_new: 0, + error_context_debug_message: 0, + error_context_drop: 0, }; // Convenience functions for checked addition and multiplication. @@ -138,6 +224,33 @@ impl VMComponentOffsets

{ size(builtins) = ret.ptr.size(), size(store) = cmul(2, ret.ptr.size()), size(limits) = ret.ptr.size(), + size(task_backpressure) = ret.ptr.size(), + size(task_return) = ret.ptr.size(), + size(task_wait) = ret.ptr.size(), + size(task_poll) = ret.ptr.size(), + size(task_yield) = ret.ptr.size(), + size(subtask_drop) = ret.ptr.size(), + size(async_enter) = ret.ptr.size(), + size(async_exit) = ret.ptr.size(), + size(future_new) = ret.ptr.size(), + size(future_write) = ret.ptr.size(), + size(future_read) = ret.ptr.size(), + size(future_cancel_write) = ret.ptr.size(), + size(future_cancel_read) = ret.ptr.size(), + size(future_close_writable) = ret.ptr.size(), + size(future_close_readable) = ret.ptr.size(), + size(stream_new) = ret.ptr.size(), + size(stream_write) = ret.ptr.size(), + size(stream_read) = ret.ptr.size(), + size(stream_cancel_write) = ret.ptr.size(), + size(stream_cancel_read) = ret.ptr.size(), + size(stream_close_writable) = ret.ptr.size(), + size(stream_close_readable) = ret.ptr.size(), + size(flat_stream_write) = ret.ptr.size(), + size(flat_stream_read) = ret.ptr.size(), + size(error_context_new) = ret.ptr.size(), + size(error_context_debug_message) = ret.ptr.size(), + size(error_context_drop) = ret.ptr.size(), align(16), size(flags) = cmul(ret.num_runtime_component_instances, ret.ptr.size_of_vmglobal_definition()), align(u32::from(ret.ptr.size())), @@ -145,6 +258,7 @@ impl VMComponentOffsets

{ size(lowerings) = cmul(ret.num_lowerings, ret.ptr.size() * 2), size(memories) = cmul(ret.num_runtime_memories, ret.ptr.size()), size(reallocs) = cmul(ret.num_runtime_reallocs, ret.ptr.size()), + size(callbacks) = cmul(ret.num_runtime_callbacks, ret.ptr.size()), size(post_returns) = cmul(ret.num_runtime_post_returns, ret.ptr.size()), size(resource_destructors) = cmul(ret.num_resources, ret.ptr.size()), } @@ -215,6 +329,141 @@ impl VMComponentOffsets

{ self.lowerings } + /// The offset of the `task_backpressure` field. + pub fn task_backpressure(&self) -> u32 { + self.task_backpressure + } + + /// The offset of the `task_return` field. + pub fn task_return(&self) -> u32 { + self.task_return + } + + /// The offset of the `task_wait` field. + pub fn task_wait(&self) -> u32 { + self.task_wait + } + + /// The offset of the `task_poll` field. + pub fn task_poll(&self) -> u32 { + self.task_poll + } + + /// The offset of the `task_yield` field. + pub fn task_yield(&self) -> u32 { + self.task_yield + } + + /// The offset of the `subtask_drop` field. + pub fn subtask_drop(&self) -> u32 { + self.subtask_drop + } + + /// The offset of the `async_enter` field. + pub fn async_enter(&self) -> u32 { + self.async_enter + } + + /// The offset of the `async_exit` field. + pub fn async_exit(&self) -> u32 { + self.async_exit + } + + /// The offset of the `future_new` field. + pub fn future_new(&self) -> u32 { + self.future_new + } + + /// The offset of the `future_write` field. + pub fn future_write(&self) -> u32 { + self.future_write + } + + /// The offset of the `future_read` field. + pub fn future_read(&self) -> u32 { + self.future_read + } + + /// The offset of the `future_cancel_write` field. + pub fn future_cancel_write(&self) -> u32 { + self.future_cancel_write + } + + /// The offset of the `future_cancel_read` field. + pub fn future_cancel_read(&self) -> u32 { + self.future_cancel_read + } + + /// The offset of the `future_close_writable` field. + pub fn future_close_writable(&self) -> u32 { + self.future_close_writable + } + + /// The offset of the `future_close_readable` field. + pub fn future_close_readable(&self) -> u32 { + self.future_close_readable + } + + /// The offset of the `stream_new` field. + pub fn stream_new(&self) -> u32 { + self.stream_new + } + + /// The offset of the `stream_write` field. + pub fn stream_write(&self) -> u32 { + self.stream_write + } + + /// The offset of the `stream_read` field. + pub fn stream_read(&self) -> u32 { + self.stream_read + } + + /// The offset of the `stream_cancel_write` field. + pub fn stream_cancel_write(&self) -> u32 { + self.stream_cancel_write + } + + /// The offset of the `stream_cancel_read` field. + pub fn stream_cancel_read(&self) -> u32 { + self.stream_cancel_read + } + + /// The offset of the `stream_close_writable` field. + pub fn stream_close_writable(&self) -> u32 { + self.stream_close_writable + } + + /// The offset of the `stream_close_readable` field. + pub fn stream_close_readable(&self) -> u32 { + self.stream_close_readable + } + + /// The offset of the `flat_stream_write` field. + pub fn flat_stream_write(&self) -> u32 { + self.flat_stream_write + } + + /// The offset of the `flat_stream_read` field. + pub fn flat_stream_read(&self) -> u32 { + self.flat_stream_read + } + + /// The offset of the `error_context_new` field. + pub fn error_context_new(&self) -> u32 { + self.error_context_new + } + + /// The offset of the `error_context_debug_message` field. + pub fn error_context_debug_message(&self) -> u32 { + self.error_context_debug_message + } + + /// The offset of the `error_context_drop` field. + pub fn error_context_drop(&self) -> u32 { + self.error_context_drop + } + /// The offset of the `VMLowering` for the `index` specified. #[inline] pub fn lowering(&self, index: LoweredIndex) -> u32 { @@ -280,6 +529,20 @@ impl VMComponentOffsets

{ self.runtime_reallocs() + index.as_u32() * u32::from(self.ptr.size()) } + /// The offset of the base of the `runtime_callbacks` field + #[inline] + pub fn runtime_callbacks(&self) -> u32 { + self.callbacks + } + + /// The offset of the `*mut VMFuncRef` for the runtime index + /// provided. + #[inline] + pub fn runtime_callback(&self, index: RuntimeCallbackIndex) -> u32 { + assert!(index.as_u32() < self.num_runtime_callbacks); + self.runtime_callbacks() + index.as_u32() * u32::from(self.ptr.size()) + } + /// The offset of the base of the `runtime_post_returns` field #[inline] pub fn runtime_post_returns(&self) -> u32 { diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index 4afdac971ff7..4dc3ec13902c 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -21,7 +21,7 @@ use crate::component::dfg::CoreDef; use crate::component::{ Adapter, AdapterOptions as AdapterOptionsDfg, ComponentTypesBuilder, FlatType, InterfaceType, - StringEncoding, Transcode, TypeFuncIndex, + RuntimeComponentInstanceIndex, StringEncoding, Transcode, TypeFuncIndex, }; use crate::fact::transcode::Transcoder; use crate::prelude::*; @@ -64,6 +64,11 @@ pub struct Module<'a> { imported_resource_transfer_borrow: Option, imported_resource_enter_call: Option, imported_resource_exit_call: Option, + imported_async_enter_call: Option, + imported_async_exit_call: Option, + imported_future_transfer: Option, + imported_stream_transfer: Option, + imported_error_context_transfer: Option, // Current status of index spaces from the imports generated so far. imported_funcs: PrimaryMap>, @@ -73,6 +78,11 @@ pub struct Module<'a> { funcs: PrimaryMap, helper_funcs: HashMap, helper_worklist: Vec<(FunctionId, Helper)>, + + globals_by_type: [Vec; 4], + globals: Vec, + + exports: Vec<(u32, String)>, } struct AdapterData { @@ -95,6 +105,7 @@ struct AdapterData { /// These options are typically unique per-adapter and generally aren't needed /// when translating recursive types within an adapter. struct AdapterOptions { + instance: RuntimeComponentInstanceIndex, /// The ascribed type of this adapter. ty: TypeFuncIndex, /// The global that represents the instance flags for where this adapter @@ -122,6 +133,8 @@ struct Options { /// An optionally-specified function to be used to allocate space for /// types such as strings as they go into a module. realloc: Option, + callback: Option, + async_: bool, } enum Context { @@ -187,6 +200,14 @@ impl<'a> Module<'a> { imported_resource_transfer_borrow: None, imported_resource_enter_call: None, imported_resource_exit_call: None, + imported_async_enter_call: None, + imported_async_exit_call: None, + imported_future_transfer: None, + imported_stream_transfer: None, + imported_error_context_transfer: None, + globals_by_type: Default::default(), + globals: Default::default(), + exports: Vec::new(), } } @@ -240,6 +261,28 @@ impl<'a> Module<'a> { } } + fn allocate(&mut self, counts: &mut [usize; 4], ty: ValType) -> u32 { + let which = match ty { + ValType::I32 => 0, + ValType::I64 => 1, + ValType::F32 => 2, + ValType::F64 => 3, + _ => unreachable!(), + }; + + let index = counts[which]; + counts[which] += 1; + + if let Some(offset) = self.globals_by_type[which].get(index) { + *offset + } else { + let offset = u32::try_from(self.globals.len()).unwrap(); + self.globals_by_type[which].push(offset); + self.globals.push(ty); + offset + } + } + fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions { let AdapterOptionsDfg { instance, @@ -248,7 +291,10 @@ impl<'a> Module<'a> { memory64, realloc, post_return: _, // handled above + callback, + async_, } = options; + let flags = self.import_global( "flags", &format!("instance{}", instance.as_u32()), @@ -287,8 +333,26 @@ impl<'a> Module<'a> { func.clone(), ) }); + let callback = callback.as_ref().map(|func| { + let ptr = if *memory64 { + ValType::I64 + } else { + ValType::I32 + }; + let ty = self.core_types.function( + &[ptr, ValType::I32, ValType::I32, ValType::I32], + &[ValType::I32], + ); + self.import_func( + "callback", + &format!("f{}", self.imported_funcs.len()), + ty, + func.clone(), + ) + }); AdapterOptions { + instance: *instance, ty, flags, post_return: None, @@ -297,6 +361,8 @@ impl<'a> Module<'a> { memory64: *memory64, memory, realloc, + callback, + async_: *async_, }, } } @@ -397,6 +463,78 @@ impl<'a> Module<'a> { idx } + fn import_async_enter_call(&mut self) -> FuncIndex { + self.import_simple( + "async", + "enter-call", + &[ + ValType::FUNCREF, + ValType::FUNCREF, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ], + &[], + Import::AsyncEnterCall, + |me| &mut me.imported_async_enter_call, + ) + } + + fn import_async_exit_call(&mut self, callback: Option) -> FuncIndex { + self.import_simple( + "async", + "exit-call", + &[ + ValType::I32, + ValType::FUNCREF, + ValType::I32, + ValType::I32, + ValType::I32, + ValType::I32, + ], + &[ValType::I32], + Import::AsyncExitCall( + callback + .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()), + ), + |me| &mut me.imported_async_exit_call, + ) + } + + fn import_future_transfer(&mut self) -> FuncIndex { + self.import_simple( + "future", + "transfer", + &[ValType::I32; 3], + &[ValType::I32], + Import::FutureTransfer, + |me| &mut me.imported_future_transfer, + ) + } + + fn import_stream_transfer(&mut self) -> FuncIndex { + self.import_simple( + "stream", + "transfer", + &[ValType::I32; 3], + &[ValType::I32], + Import::StreamTransfer, + |me| &mut me.imported_stream_transfer, + ) + } + + fn import_error_context_transfer(&mut self) -> FuncIndex { + self.import_simple( + "error-context", + "transfer", + &[ValType::I32; 3], + &[ValType::I32], + Import::ErrorContextTransfer, + |me| &mut me.imported_error_context_transfer, + ) + } + fn import_resource_transfer_own(&mut self) -> FuncIndex { self.import_simple( "resource", @@ -472,6 +610,11 @@ impl<'a> Module<'a> { exports.export(name, ExportKind::Func, idx.as_u32()); } } + for (idx, name) in &self.exports { + exports.export(name, ExportKind::Func, *idx); + } + + let imported_global_count = u32::try_from(self.imported_globals.len()).unwrap(); // With all functions numbered the fragments of the body of each // function can be assigned into one final adapter function. @@ -504,6 +647,15 @@ impl<'a> Module<'a> { Body::Call(id) => { Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body); } + Body::RefFunc(id) => { + Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body); + } + Body::GlobalGet(offset) => { + Instruction::GlobalGet(offset + imported_global_count).encode(&mut body); + } + Body::GlobalSet(offset) => { + Instruction::GlobalSet(offset + imported_global_count).encode(&mut body); + } } } code.raw(&body); @@ -512,10 +664,29 @@ impl<'a> Module<'a> { let traps = traps.finish(); + let mut globals = GlobalSection::new(); + for ty in &self.globals { + globals.global( + GlobalType { + val_type: *ty, + mutable: true, + shared: false, + }, + &match ty { + ValType::I32 => ConstExpr::i32_const(0), + ValType::I64 => ConstExpr::i64_const(0), + ValType::F32 => ConstExpr::f32_const(0_f32), + ValType::F64 => ConstExpr::f64_const(0_f64), + _ => unreachable!(), + }, + ); + } + let mut result = wasm_encoder::Module::new(); result.section(&self.core_types.section); result.section(&self.core_imports); result.section(&funcs); + result.section(&globals); result.section(&exports); result.section(&code); if self.debug { @@ -561,6 +732,21 @@ pub enum Import { /// Tears down a previous entry and handles checking borrow-related /// metadata. ResourceExitCall, + /// An intrinsic used by FACT-generated modules to begin a call to an + /// async-lowered import function. + AsyncEnterCall, + /// An intrinsic used by FACT-generated modules to complete a call to an + /// async-lowered import function. + AsyncExitCall(Option), + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of a `future`. + FutureTransfer, + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of a `stream`. + StreamTransfer, + /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer + /// ownership of an `error-context`. + ErrorContextTransfer, } impl Options { @@ -659,6 +845,9 @@ struct Function { enum Body { Raw(Vec, Vec<(usize, traps::Trap)>), Call(FunctionId), + RefFunc(FunctionId), + GlobalGet(u32), + GlobalSet(u32), } impl Function { diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index 328ec085e359..899fc8e1b4a7 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -13,6 +13,14 @@ pub struct Signature { pub params: Vec, /// Core wasm results. pub results: Vec, + /// Indicator to whether parameters are indirect, meaning that the first + /// entry of `params` is a pointer type which all parameters are loaded + /// through. + pub params_indirect: bool, + /// Indicator whether results are passed indirectly. This may mean that + /// `results` is an `i32` or that `params` ends with an `i32` depending on + /// the `Context`. + pub results_indirect: bool, } impl ComponentTypesBuilder { @@ -26,6 +34,16 @@ impl ComponentTypesBuilder { let ty = &self[options.ty]; let ptr_ty = options.options.ptr(); + if let (Context::Lower, true) = (&context, options.options.async_) { + return Signature { + params: vec![ptr_ty; 2], + results: vec![ValType::I32], + params_indirect: true, + results_indirect: true, + }; + } + + let mut params_indirect = false; let mut params = match self.flatten_types( &options.options, MAX_FLAT_PARAMS, @@ -33,10 +51,25 @@ impl ComponentTypesBuilder { ) { Some(list) => list, None => { + params_indirect = true; vec![ptr_ty] } }; + if options.options.async_ { + return Signature { + params, + results: if options.options.callback.is_some() { + vec![ptr_ty] + } else { + Vec::new() + }, + params_indirect, + results_indirect: false, + }; + } + + let mut results_indirect = false; let results = match self.flatten_types( &options.options, MAX_FLAT_RESULTS, @@ -44,6 +77,7 @@ impl ComponentTypesBuilder { ) { Some(list) => list, None => { + results_indirect = true; match context { // For a lifted function too-many-results gets translated to a // returned pointer where results are read from. The callee @@ -59,7 +93,70 @@ impl ComponentTypesBuilder { } } }; - Signature { params, results } + Signature { + params, + results, + params_indirect, + results_indirect, + } + } + + pub(super) fn async_start_signature(&self, options: &AdapterOptions) -> Signature { + let ty = &self[options.ty]; + let ptr_ty = options.options.ptr(); + + let mut params = vec![ptr_ty]; + + let mut results_indirect = false; + let results = match self.flatten_types( + &options.options, + MAX_FLAT_PARAMS, + self[ty.params].types.iter().copied(), + ) { + Some(list) => list, + None => { + results_indirect = true; + params.push(ptr_ty); + Vec::new() + } + }; + Signature { + params, + results, + params_indirect: false, + results_indirect, + } + } + + pub(super) fn async_return_signature(&self, options: &AdapterOptions) -> Signature { + let ty = &self[options.ty]; + let ptr_ty = options.options.ptr(); + + let mut params_indirect = false; + let mut params = match self.flatten_types( + &options.options, + if options.options.async_ { + MAX_FLAT_PARAMS + } else { + MAX_FLAT_RESULTS + }, + self[ty.results].types.iter().copied(), + ) { + Some(list) => list, + None => { + params_indirect = true; + vec![ptr_ty] + } + }; + // Add return pointer + params.push(ptr_ty); + + Signature { + params, + results: Vec::new(), + params_indirect, + results_indirect: false, + } } /// Pushes the flat version of a list of component types into a final result diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 8af700de421b..005aa7423709 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -17,9 +17,10 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, FixedEncoding as FE, FlatType, InterfaceType, - StringEncoding, Transcode, TypeEnumIndex, TypeFlagsIndex, TypeListIndex, TypeOptionIndex, - TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeTupleIndex, TypeVariantIndex, - VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + StringEncoding, Transcode, TypeEnumIndex, TypeErrorContextTableIndex, TypeFlagsIndex, + TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, + TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, + FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, }; use crate::fact::signature::Signature; use crate::fact::transcode::Transcoder; @@ -39,6 +40,9 @@ use wasmtime_component_util::{DiscriminantSize, FlagsSize}; const MAX_STRING_BYTE_LENGTH: u32 = 1 << 31; const UTF16_TAG: u32 = 1 << 31; +const EXIT_FLAG_ASYNC_CALLER: i32 = 1 << 0; +const EXIT_FLAG_ASYNC_CALLEE: i32 = 1 << 1; + /// This value is arbitrarily chosen and should be fine to change at any time, /// it just seemed like a halfway reasonable starting point. const INITIAL_FUEL: usize = 1_000; @@ -80,37 +84,168 @@ struct Compiler<'a, 'b> { } pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { - let lower_sig = module.types.signature(&adapter.lower, Context::Lower); - let lift_sig = module.types.signature(&adapter.lift, Context::Lift); - let ty = module - .core_types - .function(&lower_sig.params, &lower_sig.results); - let result = module - .funcs - .push(Function::new(Some(adapter.name.clone()), ty)); - - // If this type signature contains any borrowed resources then invocations - // of enter/exit call for resource-related metadata tracking must be used. - // It shouldn't matter whether the lower/lift signature is used here as both - // should return the same answer. - let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower); - assert_eq!( - emit_resource_call, - module.types.contains_borrow_resource(&adapter.lift) - ); - - Compiler { - types: module.types, - module, - code: Vec::new(), - nlocals: lower_sig.params.len() as u32, - free_locals: HashMap::new(), - traps: Vec::new(), - result, - fuel: INITIAL_FUEL, - emit_resource_call, + fn compiler<'a, 'b>( + module: &'b mut Module<'a>, + adapter: &AdapterData, + ) -> (Compiler<'a, 'b>, Signature, Signature) { + let lower_sig = module.types.signature(&adapter.lower, Context::Lower); + let lift_sig = module.types.signature(&adapter.lift, Context::Lift); + let ty = module + .core_types + .function(&lower_sig.params, &lower_sig.results); + let result = module + .funcs + .push(Function::new(Some(adapter.name.clone()), ty)); + + // If this type signature contains any borrowed resources then invocations + // of enter/exit call for resource-related metadata tracking must be used. + // It shouldn't matter whether the lower/lift signature is used here as both + // should return the same answer. + let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower); + assert_eq!( + emit_resource_call, + module.types.contains_borrow_resource(&adapter.lift) + ); + + ( + Compiler { + types: module.types, + module, + code: Vec::new(), + nlocals: lower_sig.params.len() as u32, + free_locals: HashMap::new(), + traps: Vec::new(), + result, + fuel: INITIAL_FUEL, + emit_resource_call, + }, + lower_sig, + lift_sig, + ) + } + + let start_adapter = |module: &mut Module, param_globals| { + let sig = module.types.async_start_signature(&adapter.lift); + let ty = module.core_types.function(&sig.params, &sig.results); + let result = module.funcs.push(Function::new( + Some(format!("[async-start]{}", adapter.name)), + ty, + )); + + Compiler { + types: module.types, + module, + code: Vec::new(), + nlocals: sig.params.len() as u32, + free_locals: HashMap::new(), + traps: Vec::new(), + result, + fuel: INITIAL_FUEL, + emit_resource_call: false, + } + .compile_async_start_adapter(adapter, &sig, param_globals); + + result + }; + + let return_adapter = |module: &mut Module, result_globals| { + let sig = module.types.async_return_signature(&adapter.lift); + let ty = module.core_types.function(&sig.params, &sig.results); + let result = module.funcs.push(Function::new( + Some(format!("[async-return]{}", adapter.name)), + ty, + )); + + Compiler { + types: module.types, + module, + code: Vec::new(), + nlocals: sig.params.len() as u32, + free_locals: HashMap::new(), + traps: Vec::new(), + result, + fuel: INITIAL_FUEL, + emit_resource_call: false, + } + .compile_async_return_adapter(adapter, &sig, result_globals); + + result + }; + + match (adapter.lower.options.async_, adapter.lift.options.async_) { + (false, false) => { + let (compiler, lower_sig, lift_sig) = compiler(module, adapter); + compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig) + } + (true, true) => { + let start = start_adapter(module, None); + let return_ = return_adapter(module, None); + let (compiler, _, lift_sig) = compiler(module, adapter); + compiler.compile_async_to_async_adapter( + adapter, + start, + return_, + i32::try_from(lift_sig.params.len()).unwrap(), + ); + } + (false, true) => { + let lower_sig = module.types.signature(&adapter.lower, Context::Lower); + let param_globals = if lower_sig.params_indirect { + None + } else { + let mut counts = [0; 4]; + Some( + lower_sig + .params + .iter() + .take(if lower_sig.results_indirect { + lower_sig.params.len() - 1 + } else { + lower_sig.params.len() + }) + .map(|ty| module.allocate(&mut counts, *ty)) + .collect::>(), + ) + }; + let result_globals = if lower_sig.results_indirect { + None + } else { + let mut counts = [0; 4]; + Some( + lower_sig + .results + .iter() + .map(|ty| module.allocate(&mut counts, *ty)) + .collect::>(), + ) + }; + + let start = start_adapter(module, param_globals.as_deref()); + let return_ = return_adapter(module, result_globals.as_deref()); + let (compiler, _, lift_sig) = compiler(module, adapter); + compiler.compile_sync_to_async_adapter( + adapter, + start, + return_, + i32::try_from(lift_sig.params.len()).unwrap(), + param_globals.as_deref(), + result_globals.as_deref(), + ); + } + (true, false) => { + let lift_sig = module.types.signature(&adapter.lift, Context::Lift); + let start = start_adapter(module, None); + let return_ = return_adapter(module, None); + let (compiler, ..) = compiler(module, adapter); + compiler.compile_async_to_sync_adapter( + adapter, + start, + return_, + i32::try_from(lift_sig.params.len()).unwrap(), + i32::try_from(lift_sig.results.len()).unwrap(), + ); + } } - .compile_adapter(adapter, &lower_sig, &lift_sig) } /// Compiles a helper function as specified by the `Helper` configuration. @@ -244,7 +379,292 @@ struct Memory<'a> { } impl Compiler<'_, '_> { - fn compile_adapter( + fn compile_async_to_async_adapter( + mut self, + adapter: &AdapterData, + start: FunctionId, + return_: FunctionId, + param_count: i32, + ) { + let enter = self.module.import_async_enter_call(); + let exit = self + .module + .import_async_exit_call(adapter.lift.options.callback); + + self.flush_code(); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(start)); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(return_)); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const( + i32::try_from({ + let ty = &self.types[adapter.lift.ty]; + if adapter.lift.options.memory64 { + ty.task_return_type64.as_u32() + } else { + ty.task_return_type32.as_u32() + } + }) + .unwrap(), + )); + self.instruction(LocalGet(0)); + self.instruction(LocalGet(1)); + self.instruction(Call(enter.as_u32())); + + // TODO: As an optimization, consider checking the backpressure flag on the callee instance and, if it's + // unset _and_ the callee uses a callback, translate the params and call the callee function directly here + // (and make sure `exit` knows _not_ to call it in that case). + + self.module.exports.push(( + adapter.callee.as_u32(), + format!("[adapter-callee]{}", adapter.name), + )); + + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(RefFunc(adapter.callee.as_u32())); + self.instruction(I32Const( + i32::try_from(adapter.lift.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const(param_count)); + self.instruction(I32Const(1)); // leave room for the guest context result + self.instruction(I32Const(EXIT_FLAG_ASYNC_CALLER | EXIT_FLAG_ASYNC_CALLEE)); + self.instruction(Call(exit.as_u32())); + + self.finish() + } + + fn compile_sync_to_async_adapter( + mut self, + adapter: &AdapterData, + start: FunctionId, + return_: FunctionId, + param_count: i32, + param_globals: Option<&[u32]>, + result_globals: Option<&[u32]>, + ) { + let enter = self.module.import_async_enter_call(); + let exit = self + .module + .import_async_exit_call(adapter.lift.options.callback); + + self.flush_code(); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(start)); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(return_)); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const( + i32::try_from({ + let ty = &self.types[adapter.lift.ty]; + if adapter.lift.options.memory64 { + ty.task_return_type64.as_u32() + } else { + ty.task_return_type32.as_u32() + } + }) + .unwrap(), + )); + + let results_local = if let Some(globals) = param_globals { + for (local, global) in globals.iter().enumerate() { + self.instruction(LocalGet(u32::try_from(local).unwrap())); + self.flush_code(); + self.module.funcs[self.result] + .body + .push(Body::GlobalSet(*global)); + } + self.instruction(I32Const(0)); // dummy params pointer + u32::try_from(globals.len()).unwrap() + } else { + self.instruction(LocalGet(0)); + 1 + }; + + if result_globals.is_some() { + self.instruction(I32Const(0)); // dummy results pointer + } else { + self.instruction(LocalGet(results_local)); + } + + self.instruction(Call(enter.as_u32())); + + // TODO: As an optimization, consider checking the backpressure flag on the callee instance and, if it's + // unset _and_ the callee uses a callback, translate the params and call the callee function directly here + // (and make sure `exit` knows _not_ to call it in that case). + + self.module.exports.push(( + adapter.callee.as_u32(), + format!("[adapter-callee]{}", adapter.name), + )); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(RefFunc(adapter.callee.as_u32())); + self.instruction(I32Const( + i32::try_from(adapter.lift.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const(param_count)); + self.instruction(I32Const(1)); // leave room for the guest context result + self.instruction(I32Const(EXIT_FLAG_ASYNC_CALLEE)); + self.instruction(Call(exit.as_u32())); + self.instruction(Drop); + + if let Some(globals) = result_globals { + self.flush_code(); + for global in globals { + self.module.funcs[self.result] + .body + .push(Body::GlobalGet(*global)); + } + } + + self.finish() + } + + fn compile_async_to_sync_adapter( + mut self, + adapter: &AdapterData, + start: FunctionId, + return_: FunctionId, + param_count: i32, + result_count: i32, + ) { + let enter = self.module.import_async_enter_call(); + let exit = self.module.import_async_exit_call(None); + + self.flush_code(); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(start)); + self.module.funcs[self.result] + .body + .push(Body::RefFunc(return_)); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const( + i32::try_from({ + let ty = &self.types[adapter.lift.ty]; + if adapter.lift.options.memory64 { + ty.task_return_type64.as_u32() + } else { + ty.task_return_type32.as_u32() + } + }) + .unwrap(), + )); + self.instruction(LocalGet(0)); + self.instruction(LocalGet(1)); + self.instruction(Call(enter.as_u32())); + self.module.exports.push(( + adapter.callee.as_u32(), + format!("[adapter-callee]{}", adapter.name), + )); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(RefFunc(adapter.callee.as_u32())); + self.instruction(I32Const( + i32::try_from(adapter.lift.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const(param_count)); + self.instruction(I32Const(result_count)); + self.instruction(I32Const(EXIT_FLAG_ASYNC_CALLER)); + self.instruction(Call(exit.as_u32())); + + self.finish() + } + + fn compile_async_start_adapter( + mut self, + adapter: &AdapterData, + sig: &Signature, + param_globals: Option<&[u32]>, + ) { + let mut temps = Vec::new(); + let param_locals = if let Some(globals) = param_globals { + for global in globals { + let ty = self.module.globals[usize::try_from(*global).unwrap()]; + + self.flush_code(); + self.module.funcs[self.result] + .body + .push(Body::GlobalGet(*global)); + temps.push(self.local_set_new_tmp(ty)); + } + temps + .iter() + .map(|t| (t.idx, t.ty)) + .chain(if sig.results_indirect { + sig.params + .iter() + .enumerate() + .map(|(i, ty)| (i as u32, *ty)) + .last() + } else { + None + }) + .collect::>() + } else { + sig.params + .iter() + .enumerate() + .map(|(i, ty)| (i as u32, *ty)) + .collect::>() + }; + + self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false); + self.translate_params(adapter, ¶m_locals); + self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true); + + for tmp in temps { + self.free_temp_local(tmp); + } + + self.finish(); + } + + fn compile_async_return_adapter( + mut self, + adapter: &AdapterData, + sig: &Signature, + result_globals: Option<&[u32]>, + ) { + let param_locals = sig + .params + .iter() + .enumerate() + .map(|(i, ty)| (i as u32, *ty)) + .collect::>(); + + self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false); + self.translate_results(adapter, ¶m_locals, ¶m_locals); + self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true); + + if let Some(globals) = result_globals { + self.flush_code(); + for global in globals { + self.module.funcs[self.result] + .body + .push(Body::GlobalSet(*global)); + } + } + + self.finish() + } + + fn compile_sync_to_sync_adapter( mut self, adapter: &AdapterData, lower_sig: &Signature, @@ -362,9 +782,12 @@ impl Compiler<'_, '_> { // TODO: handle subtyping assert_eq!(src_tys.len(), dst_tys.len()); - let src_flat = + let src_flat = if adapter.lower.options.async_ { + None + } else { self.types - .flatten_types(lower_opts, MAX_FLAT_PARAMS, src_tys.iter().copied()); + .flatten_types(lower_opts, MAX_FLAT_PARAMS, src_tys.iter().copied()) + }; let dst_flat = self.types .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied()); @@ -391,16 +814,28 @@ impl Compiler<'_, '_> { let dst = if let Some(flat) = &dst_flat { Destination::Stack(flat, lift_opts) } else { - // If there are too many parameters then space is allocated in the - // destination module for the parameters via its `realloc` function. - let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t))); - let (size, align) = if lift_opts.memory64 { - (abi.size64, abi.align64) + if lift_opts.async_ { + let align = dst_tys + .iter() + .map(|t| self.types.align(lift_opts, t)) + .max() + .unwrap_or(1); + let (addr, ty) = *param_locals.last().expect("no retptr"); + assert_eq!(ty, lift_opts.ptr()); + Destination::Memory(self.memory_operand(lift_opts, TempLocal::new(addr, ty), align)) } else { - (abi.size32, abi.align32) - }; - let size = MallocSize::Const(size); - Destination::Memory(self.malloc(lift_opts, size, align)) + // If there are too many parameters then space is allocated in the + // destination module for the parameters via its `realloc` function. + let abi = + CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t))); + let (size, align) = if lift_opts.memory64 { + (abi.size64, abi.align64) + } else { + (abi.size32, abi.align32) + }; + let size = MallocSize::Const(size); + Destination::Memory(self.malloc(lift_opts, size, align)) + } }; let srcs = src @@ -416,7 +851,7 @@ impl Compiler<'_, '_> { // If the destination was linear memory instead of the stack then the // actual parameter that we're passing is the address of the values // stored, so ensure that's happening in the wasm body here. - if let Destination::Memory(mem) = dst { + if let (Destination::Memory(mem), false) = (dst, lift_opts.async_) { self.instruction(LocalGet(mem.addr.idx)); self.free_temp_local(mem.addr); } @@ -443,12 +878,21 @@ impl Compiler<'_, '_> { let lift_opts = &adapter.lift.options; let lower_opts = &adapter.lower.options; - let src_flat = - self.types - .flatten_types(lift_opts, MAX_FLAT_RESULTS, src_tys.iter().copied()); - let dst_flat = + let src_flat = self.types.flatten_types( + lift_opts, + if lift_opts.async_ { + MAX_FLAT_PARAMS + } else { + MAX_FLAT_RESULTS + }, + src_tys.iter().copied(), + ); + let dst_flat = if lower_opts.async_ { + None + } else { self.types - .flatten_types(lower_opts, MAX_FLAT_RESULTS, dst_tys.iter().copied()); + .flatten_types(lower_opts, MAX_FLAT_RESULTS, dst_tys.iter().copied()) + }; let src = if src_flat.is_some() { Source::Stack(Stack { @@ -465,7 +909,7 @@ impl Compiler<'_, '_> { .map(|t| self.types.align(lift_opts, t)) .max() .unwrap_or(1); - assert_eq!(result_locals.len(), 1); + assert_eq!(result_locals.len(), if lower_opts.async_ { 2 } else { 1 }); let (addr, ty) = result_locals[0]; assert_eq!(ty, lift_opts.ptr()); Source::Memory(self.memory_operand(lift_opts, TempLocal::new(addr, ty), align)) @@ -587,7 +1031,11 @@ impl Compiler<'_, '_> { InterfaceType::Option(_) | InterfaceType::Result(_) => 2, // TODO(#6696) - something nonzero, is 1 right? - InterfaceType::Own(_) | InterfaceType::Borrow(_) => 1, + InterfaceType::Own(_) + | InterfaceType::Borrow(_) + | InterfaceType::Future(_) + | InterfaceType::Stream(_) + | InterfaceType::ErrorContext(_) => 1, }; match self.fuel.checked_sub(cost) { @@ -622,6 +1070,11 @@ impl Compiler<'_, '_> { InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst), InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst), InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst), + InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst), + InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst), + InterfaceType::ErrorContext(t) => { + self.translate_error_context_context(*t, src, dst_ty, dst) + } } } @@ -2448,6 +2901,51 @@ impl Compiler<'_, '_> { } } + fn translate_future( + &mut self, + src_ty: TypeFutureTableIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let dst_ty = match dst_ty { + InterfaceType::Future(t) => *t, + _ => panic!("expected a `Future`"), + }; + let transfer = self.module.import_future_transfer(); + self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer); + } + + fn translate_stream( + &mut self, + src_ty: TypeStreamTableIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let dst_ty = match dst_ty { + InterfaceType::Stream(t) => *t, + _ => panic!("expected a `Stream`"), + }; + let transfer = self.module.import_stream_transfer(); + self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer); + } + + fn translate_error_context_context( + &mut self, + src_ty: TypeErrorContextTableIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let dst_ty = match dst_ty { + InterfaceType::ErrorContext(t) => *t, + _ => panic!("expected an `ErrorContext`"), + }; + let transfer = self.module.import_error_context_transfer(); + self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer); + } + fn translate_own( &mut self, src_ty: TypeResourceTableIndex, @@ -2460,7 +2958,7 @@ impl Compiler<'_, '_> { _ => panic!("expected an `Own`"), }; let transfer = self.module.import_resource_transfer_own(); - self.translate_resource(src_ty, src, dst_ty, dst, transfer); + self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer); } fn translate_borrow( @@ -2476,7 +2974,7 @@ impl Compiler<'_, '_> { }; let transfer = self.module.import_resource_transfer_borrow(); - self.translate_resource(src_ty, src, dst_ty, dst, transfer); + self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer); } /// Translates the index `src`, which resides in the table `src_ty`, into @@ -2486,11 +2984,11 @@ impl Compiler<'_, '_> { /// cranelift-generated trampoline to satisfy this import will call. The /// `transfer` function is an imported function which takes the src, src_ty, /// and dst_ty, and returns the dst index. - fn translate_resource( + fn translate_handle( &mut self, - src_ty: TypeResourceTableIndex, + src_ty: u32, src: &Source<'_>, - dst_ty: TypeResourceTableIndex, + dst_ty: u32, dst: &Destination, transfer: FuncIndex, ) { @@ -2499,8 +2997,8 @@ impl Compiler<'_, '_> { Source::Memory(mem) => self.i32_load(mem), Source::Stack(stack) => self.stack_get(stack, ValType::I32), } - self.instruction(I32Const(src_ty.as_u32() as i32)); - self.instruction(I32Const(dst_ty.as_u32() as i32)); + self.instruction(I32Const(src_ty as i32)); + self.instruction(I32Const(dst_ty as i32)); self.instruction(Call(transfer.as_u32())); match dst { Destination::Memory(mem) => self.i32_store(mem), diff --git a/crates/environ/src/trap_encoding.rs b/crates/environ/src/trap_encoding.rs index 0006d3e3ec9a..acb1f04db454 100644 --- a/crates/environ/src/trap_encoding.rs +++ b/crates/environ/src/trap_encoding.rs @@ -88,7 +88,10 @@ pub enum Trap { /// would have violated the reentrance rules of the component model, /// triggering a trap instead. CannotEnterComponent, - // if adding a variant here be sure to update the `check!` macro below + + /// Async-lifted export failed to produce a result by calling `task.return` + /// before returning `STATUS_DONE` and/or after all host tasks completed. + NoAsyncResult, // if adding a variant here be sure to update the `check!` macro below } impl Trap { @@ -124,6 +127,7 @@ impl Trap { AllocationTooLarge CastFailure CannotEnterComponent + NoAsyncResult } None @@ -154,6 +158,7 @@ impl fmt::Display for Trap { AllocationTooLarge => "allocation size too large", CastFailure => "cast failure", CannotEnterComponent => "cannot enter component instance", + NoAsyncResult => "async-lifted export failed to produce a result", }; write!(f, "wasm trap: {desc}") } diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs index b184fa28e7a9..e3df7a2cceb9 100644 --- a/crates/fuzzing/src/generators/component_types.rs +++ b/crates/fuzzing/src/generators/component_types.rs @@ -108,8 +108,10 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar .collect::>()?, ), - // Resources aren't fuzzed at this time. - Type::Own(_) | Type::Borrow(_) => unreachable!(), + // Resources, futures, streams, and error contexts aren't fuzzed at this time. + Type::Own(_) | Type::Borrow(_) | Type::Future(_) | Type::Stream(_) | Type::ErrorContext => { + unreachable!() + } }) } @@ -120,8 +122,25 @@ pub fn static_api_test<'a, P, R>( declarations: &Declarations, ) -> arbitrary::Result<()> where - P: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static, - R: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static, + P: ComponentNamedList + + Lift + + Lower + + Clone + + PartialEq + + Debug + + Arbitrary<'a> + + Send + + 'static, + R: ComponentNamedList + + Lift + + Lower + + Clone + + PartialEq + + Debug + + Arbitrary<'a> + + Send + + Sync + + 'static, { crate::init_fuzzing(); @@ -139,7 +158,7 @@ where .root() .func_wrap( IMPORT_FUNCTION, - |cx: StoreContextMut<'_, Box>, params: P| { + |cx: StoreContextMut<'_, Box>, params: P| { log::trace!("received parameters {params:?}"); let data: &(P, R) = cx.data().downcast_ref().unwrap(); let (expected_params, result) = data; @@ -149,7 +168,7 @@ where }, ) .unwrap(); - let mut store: Store> = Store::new(&engine, Box::new(())); + let mut store: Store> = Store::new(&engine, Box::new(())); let instance = linker.instantiate(&mut store, &component).unwrap(); let func = instance .get_typed_func::(&mut store, EXPORT_FUNCTION) diff --git a/crates/misc/component-async-tests/Cargo.toml b/crates/misc/component-async-tests/Cargo.toml new file mode 100644 index 000000000000..80cfe4da8274 --- /dev/null +++ b/crates/misc/component-async-tests/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "component-async-tests" +authors = ["The Wasmtime Project Developers"] +license = "Apache-2.0 WITH LLVM-exception" +version = "0.0.0" +edition.workspace = true +rust-version.workspace = true +publish = false + +[dev-dependencies] +anyhow = { workspace = true } +flate2 = "1.0.30" +futures = { workspace = true } +pretty_env_logger = { workspace = true } +tempfile = { workspace = true } +test-programs-artifacts = { workspace = true } +tokio = { workspace = true, features = ["fs", "process", "macros", "rt-multi-thread", "time"] } +wasi-http-draft = { path = "http" } +wasm-compose = { workspace = true } +wasmparser = { workspace = true } +wasmtime = { workspace = true, features = ["component-model-async"] } +wasmtime-wasi = { workspace = true } + diff --git a/crates/misc/component-async-tests/http/Cargo.toml b/crates/misc/component-async-tests/http/Cargo.toml new file mode 100644 index 000000000000..41299699f09a --- /dev/null +++ b/crates/misc/component-async-tests/http/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasi-http-draft" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +futures = { workspace = true } +wasmtime = { workspace = true, features = ["component-model-async"] } diff --git a/crates/misc/component-async-tests/http/src/lib.rs b/crates/misc/component-async-tests/http/src/lib.rs new file mode 100644 index 000000000000..930fcded6c11 --- /dev/null +++ b/crates/misc/component-async-tests/http/src/lib.rs @@ -0,0 +1,565 @@ +#![deny(warnings)] + +wasmtime::component::bindgen!({ + trappable_imports: true, + path: "../wit", + interfaces: " + import wasi:http/types@0.3.0-draft; + import wasi:http/handler@0.3.0-draft; + ", + concurrent_imports: true, + async: { + only_imports: [ + "wasi:http/types@0.3.0-draft#[static]body.finish", + "wasi:http/handler@0.3.0-draft#handle", + ] + }, + with: { + "wasi:http/types/body": Body, + "wasi:http/types/request": Request, + "wasi:http/types/request-options": RequestOptions, + "wasi:http/types/response": Response, + "wasi:http/types/fields": Fields, + } +}); + +use { + anyhow::anyhow, + std::{fmt, future::Future, mem}, + wasi::http::types::{ErrorCode, HeaderError, Method, RequestOptionsError, Scheme}, + wasmtime::{ + component::{ + self, ErrorContext, FutureReader, Linker, Resource, ResourceTable, StreamReader, + }, + AsContextMut, StoreContextMut, + }, +}; + +impl fmt::Display for Scheme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Scheme::Http => "http", + Scheme::Https => "https", + Scheme::Other(s) => s, + } + ) + } +} + +pub trait WasiHttpView: Send + Sized { + type Data; + + fn table(&mut self) -> &mut ResourceTable; + + fn send_request( + store: StoreContextMut<'_, Self::Data>, + request: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::Data>, + ) -> wasmtime::Result, ErrorCode>> + + Send + + Sync + + 'static, + > + Send + + Sync + + 'static; +} + +impl WasiHttpView for &mut T { + type Data = T::Data; + + fn table(&mut self) -> &mut ResourceTable { + (*self).table() + } + + fn send_request( + store: StoreContextMut<'_, Self::Data>, + request: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::Data>, + ) -> wasmtime::Result, ErrorCode>> + + Send + + Sync + + 'static, + > + Send + + Sync + + 'static { + T::send_request(store, request) + } +} + +pub struct WasiHttpImpl(pub T); + +impl WasiHttpView for WasiHttpImpl { + type Data = T::Data; + + fn table(&mut self) -> &mut ResourceTable { + self.0.table() + } + + fn send_request( + store: StoreContextMut<'_, Self::Data>, + request: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::Data>, + ) -> wasmtime::Result, ErrorCode>> + + Send + + Sync + + 'static, + > + Send + + Sync + + 'static { + T::send_request(store, request) + } +} + +pub struct Body { + pub stream: Option>, + pub trailers: Option>>, +} + +#[derive(Clone)] +pub struct Fields(pub Vec<(String, Vec)>); + +#[derive(Default, Copy, Clone)] +pub struct RequestOptions { + pub connect_timeout: Option, + pub first_byte_timeout: Option, + pub between_bytes_timeout: Option, +} + +pub struct Request { + pub method: Method, + pub scheme: Option, + pub path_with_query: Option, + pub authority: Option, + pub headers: Fields, + pub body: Body, + pub options: Option, +} + +pub struct Response { + pub status_code: u16, + pub headers: Fields, + pub body: Body, +} + +impl wasi::http::types::HostFields for WasiHttpImpl { + fn new(&mut self) -> wasmtime::Result> { + Ok(self.table().push(Fields(Vec::new()))?) + } + + fn from_list( + &mut self, + list: Vec<(String, Vec)>, + ) -> wasmtime::Result, HeaderError>> { + Ok(Ok(self.table().push(Fields(list))?)) + } + + fn get(&mut self, this: Resource, key: String) -> wasmtime::Result>> { + Ok(self + .table() + .get(&this)? + .0 + .iter() + .filter(|(k, _)| *k == key) + .map(|(_, v)| v.clone()) + .collect()) + } + + fn has(&mut self, this: Resource, key: String) -> wasmtime::Result { + Ok(self.table().get(&this)?.0.iter().any(|(k, _)| *k == key)) + } + + fn set( + &mut self, + this: Resource, + key: String, + values: Vec>, + ) -> wasmtime::Result> { + let fields = self.table().get_mut(&this)?; + fields.0.retain(|(k, _)| *k != key); + fields + .0 + .extend(values.into_iter().map(|v| (key.clone(), v))); + Ok(Ok(())) + } + + fn delete( + &mut self, + this: Resource, + key: String, + ) -> wasmtime::Result>, HeaderError>> { + let fields = self.table().get_mut(&this)?; + let (matched, unmatched) = mem::take(&mut fields.0) + .into_iter() + .partition(|(k, _)| *k == key); + fields.0 = unmatched; + Ok(Ok(matched.into_iter().map(|(_, v)| v).collect())) + } + + fn append( + &mut self, + this: Resource, + key: String, + value: Vec, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.0.push((key, value)); + Ok(Ok(())) + } + + fn entries(&mut self, this: Resource) -> wasmtime::Result)>> { + Ok(self.table().get(&this)?.0.clone()) + } + + fn clone(&mut self, this: Resource) -> wasmtime::Result> { + let entries = self.table().get(&this)?.0.clone(); + Ok(self.table().push(Fields(entries))?) + } + + fn drop(&mut self, this: Resource) -> wasmtime::Result<()> { + self.table().delete(this)?; + Ok(()) + } +} + +impl wasi::http::types::HostBody for WasiHttpImpl +where + T::Data: WasiHttpView, +{ + type BodyData = T::Data; + + fn new( + &mut self, + stream: StreamReader, + trailers: Option>>, + ) -> wasmtime::Result> { + Ok(self.table().push(Body { + stream: Some(stream), + trailers, + })?) + } + + fn stream(&mut self, this: Resource) -> wasmtime::Result, ()>> { + // TODO: This should return a child handle + let stream = self.table().get_mut(&this)?.stream.take().ok_or_else(|| { + anyhow!("todo: allow wasi:http/types#body.stream to be called multiple times") + })?; + + Ok(Ok(stream)) + } + + fn finish( + mut store: StoreContextMut<'_, Self::BodyData>, + this: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::BodyData>, + ) + -> wasmtime::Result>, ErrorCode>> + + 'static, + > + Send + + Sync + + 'static { + let trailers = (|| { + let trailers = store.data_mut().table().delete(this)?.trailers; + trailers + .map(|v| v.read(store.as_context_mut()).map(|v| v.into_future())) + .transpose() + })(); + async move { + let trailers = match trailers { + Ok(Some(trailers)) => Ok(trailers.await), + Ok(None) => Ok(None), + Err(e) => Err(e), + }; + + component::for_any(move |_| Ok(Ok(trailers?))) + } + } + + fn drop(&mut self, this: Resource) -> wasmtime::Result<()> { + self.table().delete(this)?; + Ok(()) + } +} + +impl wasi::http::types::HostRequest for WasiHttpImpl { + fn new( + &mut self, + headers: Resource, + body: Resource, + options: Option>, + ) -> wasmtime::Result> { + let headers = self.table().delete(headers)?; + let body = self.table().delete(body)?; + let options = if let Some(options) = options { + Some(self.table().delete(options)?) + } else { + None + }; + + Ok(self.table().push(Request { + method: Method::Get, + scheme: None, + path_with_query: None, + authority: None, + headers, + body, + options, + })?) + } + + fn method(&mut self, this: Resource) -> wasmtime::Result { + Ok(self.table().get(&this)?.method.clone()) + } + + fn set_method( + &mut self, + this: Resource, + method: Method, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.method = method; + Ok(Ok(())) + } + + fn scheme(&mut self, this: Resource) -> wasmtime::Result> { + Ok(self.table().get(&this)?.scheme.clone()) + } + + fn set_scheme( + &mut self, + this: Resource, + scheme: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.scheme = scheme; + Ok(Ok(())) + } + + fn path_with_query(&mut self, this: Resource) -> wasmtime::Result> { + Ok(self.table().get(&this)?.path_with_query.clone()) + } + + fn set_path_with_query( + &mut self, + this: Resource, + path_with_query: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.path_with_query = path_with_query; + Ok(Ok(())) + } + + fn authority(&mut self, this: Resource) -> wasmtime::Result> { + Ok(self.table().get(&this)?.authority.clone()) + } + + fn set_authority( + &mut self, + this: Resource, + authority: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.authority = authority; + Ok(Ok(())) + } + + fn options( + &mut self, + this: Resource, + ) -> wasmtime::Result>> { + // TODO: This should return an immutable child handle + let options = self.table().get(&this)?.options; + Ok(if let Some(options) = options { + Some(self.table().push(options)?) + } else { + None + }) + } + + fn headers(&mut self, this: Resource) -> wasmtime::Result> { + // TODO: This should return an immutable child handle + let headers = self.table().get(&this)?.headers.clone(); + Ok(self.table().push(headers)?) + } + + fn body(&mut self, _this: Resource) -> wasmtime::Result> { + Err(anyhow!("todo: implement wasi:http/types#request.body")) + } + + fn into_parts( + &mut self, + this: Resource, + ) -> wasmtime::Result<(Resource, Resource)> { + let request = self.table().delete(this)?; + let headers = self.table().push(request.headers)?; + let body = self.table().push(request.body)?; + Ok((headers, body)) + } + + fn drop(&mut self, this: Resource) -> wasmtime::Result<()> { + self.table().delete(this)?; + Ok(()) + } +} + +impl wasi::http::types::HostResponse for WasiHttpImpl { + fn new( + &mut self, + headers: Resource, + body: Resource, + ) -> wasmtime::Result> { + let headers = self.table().delete(headers)?; + let body = self.table().delete(body)?; + + Ok(self.table().push(Response { + status_code: 200, + headers, + body, + })?) + } + + fn status_code(&mut self, this: Resource) -> wasmtime::Result { + Ok(self.table().get(&this)?.status_code) + } + + fn set_status_code( + &mut self, + this: Resource, + status_code: u16, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.status_code = status_code; + Ok(Ok(())) + } + + fn headers(&mut self, this: Resource) -> wasmtime::Result> { + // TODO: This should return an immutable child handle + let headers = self.table().get(&this)?.headers.clone(); + Ok(self.table().push(headers)?) + } + + fn body(&mut self, _this: Resource) -> wasmtime::Result> { + Err(anyhow!("todo: implement wasi:http/types#response.body")) + } + + fn into_parts( + &mut self, + this: Resource, + ) -> wasmtime::Result<(Resource, Resource)> { + let response = self.table().delete(this)?; + let headers = self.table().push(response.headers)?; + let body = self.table().push(response.body)?; + Ok((headers, body)) + } + + fn drop(&mut self, this: Resource) -> wasmtime::Result<()> { + self.table().delete(this)?; + Ok(()) + } +} + +impl wasi::http::types::HostRequestOptions for WasiHttpImpl { + fn new(&mut self) -> wasmtime::Result> { + Ok(self.table().push(RequestOptions::default())?) + } + + fn connect_timeout(&mut self, this: Resource) -> wasmtime::Result> { + Ok(self.table().get(&this)?.connect_timeout) + } + + fn set_connect_timeout( + &mut self, + this: Resource, + connect_timeout: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.connect_timeout = connect_timeout; + Ok(Ok(())) + } + + fn first_byte_timeout( + &mut self, + this: Resource, + ) -> wasmtime::Result> { + Ok(self.table().get(&this)?.first_byte_timeout) + } + + fn set_first_byte_timeout( + &mut self, + this: Resource, + first_byte_timeout: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.first_byte_timeout = first_byte_timeout; + Ok(Ok(())) + } + + fn between_bytes_timeout( + &mut self, + this: Resource, + ) -> wasmtime::Result> { + Ok(self.table().get(&this)?.between_bytes_timeout) + } + + fn set_between_bytes_timeout( + &mut self, + this: Resource, + between_bytes_timeout: Option, + ) -> wasmtime::Result> { + self.table().get_mut(&this)?.between_bytes_timeout = between_bytes_timeout; + Ok(Ok(())) + } + + fn drop(&mut self, this: Resource) -> wasmtime::Result<()> { + self.table().delete(this)?; + Ok(()) + } +} + +impl wasi::http::types::Host for WasiHttpImpl +where + T::Data: WasiHttpView, +{ + fn http_error_code(&mut self, _error: ErrorContext) -> wasmtime::Result> { + Err(anyhow!("todo: implement wasi:http/types#http-error-code")) + } +} + +impl wasi::http::handler::Host for WasiHttpImpl { + type Data = T::Data; + + fn handle( + store: StoreContextMut<'_, Self::Data>, + request: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::Data>, + ) -> wasmtime::Result, ErrorCode>> + + Send + + Sync + + 'static, + > + Send + + Sync + + 'static { + Self::send_request(store, request) + } +} + +pub fn add_to_linker + 'static>( + linker: &mut Linker, +) -> wasmtime::Result<()> +where + ::Data: WasiHttpView, +{ + wasi::http::types::add_to_linker_get_host(linker, annotate_http(|ctx| WasiHttpImpl(ctx)))?; + wasi::http::handler::add_to_linker_get_host(linker, annotate_http(|ctx| WasiHttpImpl(ctx))) +} + +fn annotate_http(val: F) -> F +where + F: Fn(&mut T) -> WasiHttpImpl<&mut T>, +{ + val +} diff --git a/crates/misc/component-async-tests/src/lib.rs b/crates/misc/component-async-tests/src/lib.rs new file mode 100644 index 000000000000..09183e6c6c44 --- /dev/null +++ b/crates/misc/component-async-tests/src/lib.rs @@ -0,0 +1,1139 @@ +#![deny(warnings)] + +#[cfg(test)] +mod test { + use { + anyhow::{anyhow, Result}, + futures::future, + std::{ + future::Future, + ops::DerefMut, + sync::{Arc, Mutex}, + task::{Poll, Waker}, + time::Duration, + }, + tokio::fs, + transmit::exports::local::local::transmit::Control, + wasi_http_draft::{ + wasi::http::types::{Body, ErrorCode, Method, Request, Response, Scheme}, + Fields, WasiHttpView, + }, + wasm_compose::composer::ComponentComposer, + wasmparser::WasmFeatures, + wasmtime::{ + component::{ + self, Component, FutureReader, Instance, Linker, Promise, PromisesUnordered, + Resource, ResourceTable, StreamReader, StreamWriter, Val, + }, + AsContextMut, Config, Engine, Store, StoreContextMut, + }, + wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}, + }; + + macro_rules! assert_test_exists { + ($name:ident) => { + #[expect(unused_imports, reason = "just here to ensure a name exists")] + use self::$name as _; + }; + } + + test_programs_artifacts::foreach_async!(assert_test_exists); + + mod round_trip { + wasmtime::component::bindgen!({ + trappable_imports: true, + path: "wit", + world: "round-trip", + concurrent_imports: true, + concurrent_exports: true, + async: true, + }); + } + + struct Ctx { + wasi: WasiCtx, + table: ResourceTable, + #[allow(unused)] + drop_count: usize, + #[allow(unused)] + wakers: Arc>>>, + #[allow(unused)] + continue_: bool, + } + + impl WasiView for Ctx { + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } + fn ctx(&mut self) -> &mut WasiCtx { + &mut self.wasi + } + } + + impl round_trip::local::local::baz::Host for Ctx { + type Data = Ctx; + + #[allow(clippy::manual_async_fn)] + fn foo( + _: StoreContextMut<'_, Self>, + s: String, + ) -> impl Future< + Output = impl FnOnce(StoreContextMut<'_, Self>) -> wasmtime::Result + 'static, + > + Send + + 'static { + async move { + tokio::time::sleep(Duration::from_millis(10)).await; + component::for_any(move |_: StoreContextMut<'_, Self>| { + Ok(format!("{s} - entered host - exited host")) + }) + } + } + } + + async fn test_round_trip(component: &[u8], input: &str, expected_output: &str) -> Result<()> { + let mut config = Config::new(); + config.debug_info(true); + config.cranelift_debug_verifier(true); + config.wasm_component_model(true); + config.wasm_component_model_async(true); + config.async_support(true); + + let engine = Engine::new(&config)?; + + let make_store = || { + Store::new( + &engine, + Ctx { + wasi: WasiCtxBuilder::new().inherit_stdio().build(), + table: ResourceTable::default(), + drop_count: 0, + continue_: false, + wakers: Arc::new(Mutex::new(None)), + }, + ) + }; + + let component = Component::new(&engine, component)?; + + // First, test the `wasmtime-wit-bindgen` static API: + { + let mut linker = Linker::new(&engine); + + wasmtime_wasi::add_to_linker_async(&mut linker)?; + round_trip::RoundTrip::add_to_linker(&mut linker, |ctx| ctx)?; + + let mut store = make_store(); + + let round_trip = + round_trip::RoundTrip::instantiate_async(&mut store, &component, &linker).await?; + + // Start three concurrent calls and then join them all: + let mut promises = PromisesUnordered::new(); + for _ in 0..3 { + promises.push( + round_trip + .local_local_baz() + .call_foo(&mut store, input.to_owned()) + .await?, + ); + } + + while let Some(value) = promises.next(&mut store).await? { + assert_eq!(expected_output, &value); + } + } + + // Now do it again using the dynamic API (except for WASI, where we stick with the static API): + { + let mut linker = Linker::new(&engine); + + wasmtime_wasi::add_to_linker_async(&mut linker)?; + linker + .root() + .instance("local:local/baz")? + .func_new_concurrent("foo", |_, params| async move { + tokio::time::sleep(Duration::from_millis(10)).await; + component::for_any(move |_: StoreContextMut<'_, Ctx>| { + let Some(Val::String(s)) = params.into_iter().next() else { + unreachable!() + }; + Ok(vec![Val::String(format!( + "{s} - entered host - exited host" + ))]) + }) + })?; + + let mut store = make_store(); + + let instance = linker.instantiate_async(&mut store, &component).await?; + let baz_instance = instance + .get_export(&mut store, None, "local:local/baz") + .ok_or_else(|| anyhow!("can't find `local:local/baz` in instance"))?; + let foo_function = instance + .get_export(&mut store, Some(&baz_instance), "foo") + .ok_or_else(|| anyhow!("can't find `foo` in instance"))?; + let foo_function = instance + .get_func(&mut store, foo_function) + .ok_or_else(|| anyhow!("can't find `foo` in instance"))?; + + // Start three concurrent calls and then join them all: + let mut promises = PromisesUnordered::new(); + for _ in 0..3 { + promises.push( + foo_function + .call_concurrent(&mut store, vec![Val::String(input.to_owned())]) + .await?, + ); + } + + while let Some(value) = promises.next(&mut store).await? { + let Some(Val::String(value)) = value.into_iter().next() else { + unreachable!() + }; + assert_eq!(expected_output, &value); + } + } + + Ok(()) + } + + async fn compose(a: &[u8], b: &[u8]) -> Result> { + let dir = tempfile::tempdir()?; + + let a_file = dir.path().join("a.wasm"); + fs::write(&a_file, a).await?; + + let b_file = dir.path().join("b.wasm"); + fs::write(&b_file, b).await?; + + ComponentComposer::new( + &a_file, + &wasm_compose::config::Config { + dir: dir.path().to_owned(), + definitions: vec![b_file.to_owned()], + ..Default::default() + }, + WasmFeatures::WASM2 + | WasmFeatures::COMPONENT_MODEL + | WasmFeatures::COMPONENT_MODEL_ASYNC, + ) + .compose() + } + + async fn test_round_trip_uncomposed(component: &[u8]) -> Result<()> { + test_round_trip( + component, + "hello, world!", + "hello, world! - entered guest - entered host - exited host - exited guest", + ) + .await + } + + async fn test_round_trip_composed(a: &[u8], b: &[u8]) -> Result<()> { + test_round_trip( + &compose(a, b).await?, + "hello, world!", + "hello, world! - entered guest - entered guest - entered host \ + - exited host - exited guest - exited guest", + ) + .await + } + + #[tokio::test] + async fn async_round_trip_stackless() -> Result<()> { + test_round_trip_uncomposed( + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?, + ) + .await + } + + #[tokio::test] + async fn async_round_trip_stackful() -> Result<()> { + test_round_trip_uncomposed( + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?, + ) + .await + } + + #[tokio::test] + async fn async_round_trip_synchronous() -> Result<()> { + test_round_trip_uncomposed( + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?, + ) + .await + } + + #[tokio::test] + async fn async_round_trip_wait() -> Result<()> { + test_round_trip_uncomposed( + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?, + ) + .await + } + + #[tokio::test] + async fn async_round_trip_stackless_plus_stackless() -> Result<()> { + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + test_round_trip_composed(stackless, stackless).await + } + + #[tokio::test] + async fn async_round_trip_synchronous_plus_stackless() -> Result<()> { + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + test_round_trip_composed(synchronous, stackless).await + } + + #[tokio::test] + async fn async_round_trip_stackless_plus_synchronous() -> Result<()> { + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + test_round_trip_composed(stackless, synchronous).await + } + + #[tokio::test] + async fn async_round_trip_synchronous_plus_synchronous() -> Result<()> { + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + test_round_trip_composed(synchronous, synchronous).await + } + + #[tokio::test] + async fn async_round_trip_wait_plus_wait() -> Result<()> { + let wait = &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?; + test_round_trip_composed(wait, wait).await + } + + #[tokio::test] + async fn async_round_trip_synchronous_plus_wait() -> Result<()> { + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + let wait = &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?; + test_round_trip_composed(synchronous, wait).await + } + + #[tokio::test] + async fn async_round_trip_wait_plus_synchronous() -> Result<()> { + let wait = &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?; + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + test_round_trip_composed(wait, synchronous).await + } + + #[tokio::test] + async fn async_round_trip_stackless_plus_wait() -> Result<()> { + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + let wait = &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?; + test_round_trip_composed(stackless, wait).await + } + + #[tokio::test] + async fn async_round_trip_wait_plus_stackless() -> Result<()> { + let wait = &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_WAIT_COMPONENT).await?; + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + test_round_trip_composed(wait, stackless).await + } + + #[tokio::test] + async fn async_round_trip_stackful_plus_stackful() -> Result<()> { + let stackful = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?; + test_round_trip_composed(stackful, stackful).await + } + + #[tokio::test] + async fn async_round_trip_stackful_plus_stackless() -> Result<()> { + let stackful = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?; + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + test_round_trip_composed(stackful, stackless).await + } + + #[tokio::test] + async fn async_round_trip_stackless_plus_stackful() -> Result<()> { + let stackless = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKLESS_COMPONENT).await?; + let stackful = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?; + test_round_trip_composed(stackless, stackful).await + } + + #[tokio::test] + async fn async_round_trip_synchronous_plus_stackful() -> Result<()> { + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + let stackful = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?; + test_round_trip_composed(synchronous, stackful).await + } + + #[tokio::test] + async fn async_round_trip_stackful_plus_synchronous() -> Result<()> { + let stackful = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_STACKFUL_COMPONENT).await?; + let synchronous = + &fs::read(test_programs_artifacts::ASYNC_ROUND_TRIP_SYNCHRONOUS_COMPONENT).await?; + test_round_trip_composed(stackful, synchronous).await + } + + mod yield_host { + wasmtime::component::bindgen!({ + path: "wit", + world: "yield-host", + concurrent_imports: true, + concurrent_exports: true, + async: { + only_imports: [ + "local:local/ready#when-ready", + ] + }, + }); + } + + impl yield_host::local::local::continue_::Host for Ctx { + fn set_continue(&mut self, v: bool) { + self.continue_ = v; + } + + fn get_continue(&mut self) -> bool { + self.continue_ + } + } + + impl yield_host::local::local::ready::Host for Ctx { + type Data = Ctx; + + fn set_ready(&mut self, ready: bool) { + let mut wakers = self.wakers.lock().unwrap(); + if ready { + if let Some(wakers) = wakers.take() { + for waker in wakers { + waker.wake(); + } + } + } else if wakers.is_none() { + *wakers = Some(Vec::new()); + } + } + + fn when_ready( + store: StoreContextMut, + ) -> impl Future) + 'static> + + Send + + Sync + + 'static { + let wakers = store.data().wakers.clone(); + future::poll_fn(move |cx| { + let mut wakers = wakers.lock().unwrap(); + if let Some(wakers) = wakers.deref_mut() { + wakers.push(cx.waker().clone()); + Poll::Pending + } else { + Poll::Ready(component::for_any(|_| ())) + } + }) + } + } + + async fn test_run(component: &[u8]) -> Result<()> { + let mut config = Config::new(); + config.debug_info(true); + config.cranelift_debug_verifier(true); + config.wasm_component_model(true); + config.wasm_component_model_async(true); + config.async_support(true); + config.epoch_interruption(true); + + let engine = Engine::new(&config)?; + + let component = Component::new(&engine, component)?; + + let mut linker = Linker::new(&engine); + + wasmtime_wasi::add_to_linker_async(&mut linker)?; + yield_host::YieldHost::add_to_linker(&mut linker, |ctx| ctx)?; + + let mut store = Store::new( + &engine, + Ctx { + wasi: WasiCtxBuilder::new().inherit_stdio().build(), + table: ResourceTable::default(), + drop_count: 0, + continue_: false, + wakers: Arc::new(Mutex::new(None)), + }, + ); + store.set_epoch_deadline(1); + + std::thread::spawn(move || { + std::thread::sleep(Duration::from_secs(10)); + engine.increment_epoch(); + }); + + let yield_host = + yield_host::YieldHost::instantiate_async(&mut store, &component, &linker).await?; + + // Start three concurrent calls and then join them all: + let mut promises = PromisesUnordered::new(); + for _ in 0..3 { + promises.push(yield_host.local_local_run().call_run(&mut store).await?); + } + + while let Some(()) = promises.next(&mut store).await? { + // continue + } + + Ok(()) + } + + // No-op function; we only test this by composing it in `async_yield_caller` + #[allow( + dead_code, + reason = "here only to make the `assert_test_exists` macro happy" + )] + fn async_yield_callee() {} + + #[tokio::test] + async fn async_yield_caller() -> Result<()> { + let caller = &fs::read(test_programs_artifacts::ASYNC_YIELD_CALLER_COMPONENT).await?; + let callee = &fs::read(test_programs_artifacts::ASYNC_YIELD_CALLEE_COMPONENT).await?; + test_run(&compose(caller, callee).await?).await + } + + #[tokio::test] + async fn async_poll() -> Result<()> { + test_run(&fs::read(test_programs_artifacts::ASYNC_POLL_COMPONENT).await?).await + } + + // No-op function; we only test this by composing it in `async_backpressure_caller` + #[allow( + dead_code, + reason = "here only to make the `assert_test_exists` macro happy" + )] + fn async_backpressure_callee() {} + + #[tokio::test] + async fn async_backpressure_caller() -> Result<()> { + let caller = + &fs::read(test_programs_artifacts::ASYNC_BACKPRESSURE_CALLER_COMPONENT).await?; + let callee = + &fs::read(test_programs_artifacts::ASYNC_BACKPRESSURE_CALLEE_COMPONENT).await?; + test_run(&compose(caller, callee).await?).await + } + + #[tokio::test] + async fn async_transmit_caller() -> Result<()> { + let caller = &fs::read(test_programs_artifacts::ASYNC_TRANSMIT_CALLER_COMPONENT).await?; + let callee = &fs::read(test_programs_artifacts::ASYNC_TRANSMIT_CALLEE_COMPONENT).await?; + test_run(&compose(caller, callee).await?).await + } + + mod transmit { + wasmtime::component::bindgen!({ + path: "wit", + world: "transmit-callee", + concurrent_exports: true, + async: true, + }); + } + + trait TransmitTest { + type Instance; + type Params; + type Result; + + async fn instantiate( + store: impl AsContextMut, + component: &Component, + linker: &Linker, + ) -> Result; + + async fn call( + store: impl AsContextMut, + instance: &Self::Instance, + params: Self::Params, + ) -> Result>; + + fn into_params( + control: StreamReader, + caller_stream: StreamReader, + caller_future1: FutureReader, + caller_future2: FutureReader, + ) -> Self::Params; + + fn from_result( + store: impl AsContextMut, + result: Self::Result, + ) -> Result<( + StreamReader, + FutureReader, + FutureReader, + )>; + } + + struct StaticTransmitTest; + + impl TransmitTest for StaticTransmitTest { + type Instance = transmit::TransmitCallee; + type Params = ( + StreamReader, + StreamReader, + FutureReader, + FutureReader, + ); + type Result = ( + StreamReader, + FutureReader, + FutureReader, + ); + + async fn instantiate( + store: impl AsContextMut, + component: &Component, + linker: &Linker, + ) -> Result { + transmit::TransmitCallee::instantiate_async(store, component, linker).await + } + + async fn call( + store: impl AsContextMut, + instance: &Self::Instance, + params: Self::Params, + ) -> Result> { + instance + .local_local_transmit() + .call_exchange(store, params.0, params.1, params.2, params.3) + .await + } + + fn into_params( + control: StreamReader, + caller_stream: StreamReader, + caller_future1: FutureReader, + caller_future2: FutureReader, + ) -> Self::Params { + (control, caller_stream, caller_future1, caller_future2) + } + + fn from_result( + _: impl AsContextMut, + result: Self::Result, + ) -> Result<( + StreamReader, + FutureReader, + FutureReader, + )> { + Ok(result) + } + } + + struct DynamicTransmitTest; + + impl TransmitTest for DynamicTransmitTest { + type Instance = Instance; + type Params = Vec; + type Result = Val; + + async fn instantiate( + store: impl AsContextMut, + component: &Component, + linker: &Linker, + ) -> Result { + linker.instantiate_async(store, component).await + } + + async fn call( + mut store: impl AsContextMut, + instance: &Self::Instance, + params: Self::Params, + ) -> Result> { + let transmit_instance = instance + .get_export(store.as_context_mut(), None, "local:local/transmit") + .ok_or_else(|| anyhow!("can't find `local:local/transmit` in instance"))?; + let exchange_function = instance + .get_export(store.as_context_mut(), Some(&transmit_instance), "exchange") + .ok_or_else(|| anyhow!("can't find `exchange` in instance"))?; + let exchange_function = instance + .get_func(store.as_context_mut(), exchange_function) + .ok_or_else(|| anyhow!("can't find `exchange` in instance"))?; + + Ok(exchange_function + .call_concurrent(store, params) + .await? + .map(|results| results.into_iter().next().unwrap())) + } + + fn into_params( + control: StreamReader, + caller_stream: StreamReader, + caller_future1: FutureReader, + caller_future2: FutureReader, + ) -> Self::Params { + vec![ + control.into_val(), + caller_stream.into_val(), + caller_future1.into_val(), + caller_future2.into_val(), + ] + } + + fn from_result( + mut store: impl AsContextMut, + result: Self::Result, + ) -> Result<( + StreamReader, + FutureReader, + FutureReader, + )> { + let Val::Tuple(fields) = result else { + unreachable!() + }; + let stream = StreamReader::from_val(store.as_context_mut(), &fields[0])?; + let future1 = FutureReader::from_val(store.as_context_mut(), &fields[1])?; + let future2 = FutureReader::from_val(store.as_context_mut(), &fields[2])?; + Ok((stream, future1, future2)) + } + } + + async fn test_transmit(component: &[u8]) -> Result<()> { + test_transmit_with::(component).await?; + test_transmit_with::(component).await + } + + async fn test_transmit_with(component: &[u8]) -> Result<()> { + let mut config = Config::new(); + config.debug_info(true); + config.cranelift_debug_verifier(true); + config.wasm_component_model(true); + config.wasm_component_model_async(true); + config.async_support(true); + + let engine = Engine::new(&config)?; + + let make_store = || { + Store::new( + &engine, + Ctx { + wasi: WasiCtxBuilder::new().inherit_stdio().build(), + table: ResourceTable::default(), + drop_count: 0, + continue_: false, + wakers: Arc::new(Mutex::new(None)), + }, + ) + }; + + let component = Component::new(&engine, component)?; + + let mut linker = Linker::new(&engine); + + wasmtime_wasi::add_to_linker_async(&mut linker)?; + + let mut store = make_store(); + + let instance = Test::instantiate(&mut store, &component, &linker).await?; + + enum Event { + Result(Test::Result), + ControlWriteA(StreamWriter), + ControlWriteB(StreamWriter), + ControlWriteC(StreamWriter), + ControlWriteD(StreamWriter), + WriteA(StreamWriter), + WriteB, + ReadC(Option<(StreamReader, Vec)>), + ReadD(Option), + ReadNone(Option<(StreamReader, Vec)>), + } + + let (control_tx, control_rx) = component::stream(&mut store)?; + let (caller_stream_tx, caller_stream_rx) = component::stream(&mut store)?; + let (caller_future1_tx, caller_future1_rx) = component::future(&mut store)?; + let (_caller_future2_tx, caller_future2_rx) = component::future(&mut store)?; + + let mut promises = PromisesUnordered::>::new(); + let mut caller_future1_tx = Some(caller_future1_tx); + let mut callee_stream_rx = None; + let mut callee_future1_rx = None; + let mut complete = false; + + promises.push( + control_tx + .write(&mut store, vec![Control::ReadStream("a".into())])? + .map(Event::ControlWriteA), + ); + + promises.push( + caller_stream_tx + .write(&mut store, vec!["a".into()])? + .map(Event::WriteA), + ); + + promises.push( + Test::call( + &mut store, + &instance, + Test::into_params( + control_rx, + caller_stream_rx, + caller_future1_rx, + caller_future2_rx, + ), + ) + .await? + .map(Event::Result), + ); + + while let Some(event) = promises.next(&mut store).await? { + match event { + Event::Result(result) => { + let results = Test::from_result(&mut store, result)?; + callee_stream_rx = Some(results.0); + callee_future1_rx = Some(results.1); + results.2.close(&mut store)?; + } + Event::ControlWriteA(tx) => { + promises.push( + tx.write(&mut store, vec![Control::ReadFuture("b".into())])? + .map(Event::ControlWriteB), + ); + } + Event::WriteA(tx) => { + tx.close(&mut store)?; + promises.push( + caller_future1_tx + .take() + .unwrap() + .write(&mut store, "b".into())? + .map(|()| Event::WriteB), + ); + } + Event::ControlWriteB(tx) => { + promises.push( + tx.write(&mut store, vec![Control::WriteStream("c".into())])? + .map(Event::ControlWriteC), + ); + } + Event::WriteB => { + promises.push( + callee_stream_rx + .take() + .unwrap() + .read(&mut store)? + .map(Event::ReadC), + ); + } + Event::ControlWriteC(tx) => { + promises.push( + tx.write(&mut store, vec![Control::WriteFuture("d".into())])? + .map(Event::ControlWriteD), + ); + } + Event::ReadC(None) => unreachable!(), + Event::ReadC(Some((rx, values))) => { + assert_eq!("c", &values[0]); + promises.push( + callee_future1_rx + .take() + .unwrap() + .read(&mut store)? + .map(Event::ReadD), + ); + callee_stream_rx = Some(rx); + } + Event::ControlWriteD(tx) => { + tx.close(&mut store)?; + } + Event::ReadD(None) => unreachable!(), + Event::ReadD(Some(value)) => { + assert_eq!("d", &value); + promises.push( + callee_stream_rx + .take() + .unwrap() + .read(&mut store)? + .map(Event::ReadNone), + ); + } + Event::ReadNone(Some(_)) => unreachable!(), + Event::ReadNone(None) => { + complete = true; + } + } + } + + assert!(complete); + + Ok(()) + } + + #[tokio::test] + async fn async_transmit_callee() -> Result<()> { + test_transmit(&fs::read(test_programs_artifacts::ASYNC_TRANSMIT_CALLEE_COMPONENT).await?) + .await + } + + mod proxy { + wasmtime::component::bindgen!({ + path: "wit", + world: "wasi:http/proxy", + concurrent_imports: true, + concurrent_exports: true, + async: { + only_imports: [ + "wasi:http/types@0.3.0-draft#[static]body.finish", + "wasi:http/handler@0.3.0-draft#handle", + ] + }, + with: { + "wasi:http/types": wasi_http_draft::wasi::http::types, + } + }); + } + + impl WasiHttpView for Ctx { + type Data = Ctx; + + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } + + #[allow(clippy::manual_async_fn)] + fn send_request( + _store: StoreContextMut<'_, Self::Data>, + _request: Resource, + ) -> impl Future< + Output = impl FnOnce( + StoreContextMut<'_, Self::Data>, + ) + -> wasmtime::Result, ErrorCode>> + + 'static, + > + Send + + 'static { + async move { + move |_: StoreContextMut<'_, Self>| { + Err(anyhow!("no outbound request handler available")) + } + } + } + } + + async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { + use { + flate2::{ + write::{DeflateDecoder, DeflateEncoder}, + Compression, + }, + std::io::Write, + }; + + let mut config = Config::new(); + config.cranelift_debug_verifier(true); + config.wasm_component_model(true); + config.wasm_component_model_async(true); + config.async_support(true); + + let engine = Engine::new(&config)?; + + let component = Component::new(&engine, component)?; + + let mut linker = Linker::new(&engine); + + wasmtime_wasi::add_to_linker_async(&mut linker)?; + wasi_http_draft::add_to_linker(&mut linker)?; + + let mut store = Store::new( + &engine, + Ctx { + wasi: WasiCtxBuilder::new().inherit_stdio().build(), + table: ResourceTable::default(), + drop_count: 0, + continue_: false, + wakers: Arc::new(Mutex::new(None)), + }, + ); + + let proxy = proxy::Proxy::instantiate_async(&mut store, &component, &linker).await?; + + let headers = [("foo".into(), b"bar".into())]; + + let body = b"And the mome raths outgrabe"; + + enum Event { + RequestBodyWrite(StreamWriter), + RequestTrailersWrite, + Response(Result, ErrorCode>), + ResponseBodyRead(Option<(StreamReader, Vec)>), + ResponseTrailersRead(Option>), + } + + let mut promises = PromisesUnordered::new(); + + let (request_body_tx, request_body_rx) = component::stream(&mut store)?; + + promises.push( + request_body_tx + .write( + &mut store, + if use_compression { + let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast()); + encoder.write_all(body)?; + encoder.finish()? + } else { + body.to_vec() + }, + )? + .map(Event::RequestBodyWrite), + ); + + let trailers = vec![("fizz".into(), b"buzz".into())]; + + let (request_trailers_tx, request_trailers_rx) = component::future(&mut store)?; + + let request_trailers = WasiView::table(store.data_mut()).push(Fields(trailers.clone()))?; + + promises.push( + request_trailers_tx + .write(&mut store, request_trailers)? + .map(|()| Event::RequestTrailersWrite), + ); + + let request = WasiView::table(store.data_mut()).push(Request { + method: Method::Post, + scheme: Some(Scheme::Http), + path_with_query: Some("/".into()), + authority: Some("localhost".into()), + headers: Fields( + headers + .iter() + .cloned() + .chain(if use_compression { + vec![ + ("content-encoding".into(), b"deflate".into()), + ("accept-encoding".into(), b"deflate".into()), + ] + } else { + Vec::new() + }) + .collect(), + ), + body: Body { + stream: Some(request_body_rx), + trailers: Some(request_trailers_rx), + }, + options: None, + })?; + + promises.push( + proxy + .wasi_http_handler() + .call_handle(&mut store, request) + .await? + .map(Event::Response), + ); + + let mut response_body = Vec::new(); + let mut response_trailers = None; + let mut received_trailers = false; + while let Some(event) = promises.next(&mut store).await? { + match event { + Event::RequestBodyWrite(tx) => tx.close(&mut store)?, + Event::RequestTrailersWrite => {} + Event::Response(response) => { + let mut response = WasiView::table(store.data_mut()).delete(response?)?; + + assert!(response.status_code == 200); + + assert!(headers.iter().all(|(k0, v0)| response + .headers + .0 + .iter() + .any(|(k1, v1)| k0 == k1 && v0 == v1))); + + if use_compression { + assert!(response.headers.0.iter().any(|(k, v)| matches!( + (k.as_str(), v.as_slice()), + ("content-encoding", b"deflate") + ))); + } + + response_trailers = response.body.trailers.take(); + + promises.push( + response + .body + .stream + .take() + .unwrap() + .read(&mut store)? + .map(Event::ResponseBodyRead), + ); + } + Event::ResponseBodyRead(Some((rx, chunk))) => { + response_body.extend(chunk); + promises.push(rx.read(&mut store)?.map(Event::ResponseBodyRead)); + } + Event::ResponseBodyRead(None) => { + let response_body = if use_compression { + let mut decoder = DeflateDecoder::new(Vec::new()); + decoder.write_all(&response_body)?; + decoder.finish()? + } else { + response_body.clone() + }; + + assert_eq!(body as &[_], &response_body); + + promises.push( + response_trailers + .take() + .unwrap() + .read(&mut store)? + .map(Event::ResponseTrailersRead), + ); + } + Event::ResponseTrailersRead(Some(response_trailers)) => { + let response_trailers = + WasiView::table(store.data_mut()).delete(response_trailers)?; + + assert!(trailers.iter().all(|(k0, v0)| response_trailers + .0 + .iter() + .any(|(k1, v1)| k0 == k1 && v0 == v1))); + + received_trailers = true; + } + Event::ResponseTrailersRead(None) => panic!("expected response trailers; got none"), + } + } + + assert!(received_trailers); + + Ok(()) + } + + #[tokio::test] + async fn async_http_echo() -> Result<()> { + test_http_echo( + &fs::read(test_programs_artifacts::ASYNC_HTTP_ECHO_COMPONENT).await?, + false, + ) + .await + } + + #[tokio::test] + async fn async_http_middleware() -> Result<()> { + let echo = &fs::read(test_programs_artifacts::ASYNC_HTTP_ECHO_COMPONENT).await?; + let middleware = + &fs::read(test_programs_artifacts::ASYNC_HTTP_MIDDLEWARE_COMPONENT).await?; + test_http_echo(&compose(middleware, echo).await?, true).await + } +} diff --git a/crates/misc/component-async-tests/wit/deps/http/handler.wit b/crates/misc/component-async-tests/wit/deps/http/handler.wit new file mode 100644 index 000000000000..bfe459f40b26 --- /dev/null +++ b/crates/misc/component-async-tests/wit/deps/http/handler.wit @@ -0,0 +1,17 @@ +// This interface defines a handler of HTTP Requests. It may be imported by +/// components which wish to send HTTP Requests and also exported by components +/// which can respond to HTTP Requests. In addition, it may be used to pass +/// a request from one component to another without any use of a network. +interface handler { + use types.{request, response, error-code}; + + /// When exported, this function may be called with either an incoming + /// request read from the network or a request synthesized or forwarded by + /// another component. + /// + /// When imported, this function may be used to either send an outgoing + /// request over the network or pass it to another component. + handle: func( + request: request, + ) -> result; +} diff --git a/crates/misc/component-async-tests/wit/deps/http/proxy.wit b/crates/misc/component-async-tests/wit/deps/http/proxy.wit new file mode 100644 index 000000000000..efb3952134a7 --- /dev/null +++ b/crates/misc/component-async-tests/wit/deps/http/proxy.wit @@ -0,0 +1,6 @@ +package wasi:http@0.3.0-draft; + +world proxy { + import handler; + export handler; +} diff --git a/crates/misc/component-async-tests/wit/deps/http/types.wit b/crates/misc/component-async-tests/wit/deps/http/types.wit new file mode 100644 index 000000000000..4c5bd4c4eef2 --- /dev/null +++ b/crates/misc/component-async-tests/wit/deps/http/types.wit @@ -0,0 +1,424 @@ +/// This interface defines all of the types and methods for implementing HTTP +/// Requests and Responses, as well as their headers, trailers, and bodies. +interface types { + type duration = u64; + + /// This type corresponds to HTTP standard Methods. + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string) + } + + /// This type corresponds to HTTP standard Related Schemes. + variant scheme { + HTTP, + HTTPS, + other(string) + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option) + } + + /// Defines the case payload type for `DNS-error` above: + record DNS-error-payload { + rcode: option, + info-code: option + } + + /// Defines the case payload type for `TLS-alert-received` above: + record TLS-alert-received-payload { + alert-id: option, + alert-message: option + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + record field-size-payload { + field-name: option, + field-size: option + } + + /// Attempts to extract a http-related `error-code` from the stream `error` + /// provided. + /// + /// Stream operations may fail with a stream `error` with more information + /// about the operation that failed. This `error` can be passed to this + /// function to see if there's http-related information about the error to + /// return. + /// + /// Note that this function is fallible because not all stream errors are + /// http-related errors. + http-error-code: func(err: error-context) -> option; + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + variant header-error { + /// This error indicates that a `field-key` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + + /// This error indicates that a forbidden `field-key` was used when trying + /// to set a header in a `fields`. + forbidden, + + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting fields of a `request-options` resource. + variant request-options-error { + /// Indicates the specified field is not supported by this implementation. + not-supported, + + /// Indicates that the operation on the `request-options` was not permitted + /// because it is immutable. + immutable, + } + + /// Field keys are always strings. + type field-key = string; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `request.headers`) might be be immutable. In an immutable fields, the + /// `set`, `append`, and `delete` operations will fail with + /// `header-error.immutable`. + resource fields { + + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + constructor(); + + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + /// + /// The tuple is a pair of the field key, represented as a string, and + /// Value, represented as a list of bytes. In a valid Fields, all keys + /// and values are valid UTF-8 strings. However, values are not always + /// well-formed, so they are represented as a raw list of bytes. + /// + /// An error result will be returned if any header or value was + /// syntactically invalid, or if a header was forbidden. + from-list: static func( + entries: list> + ) -> result; + + /// Get all of the values corresponding to a key. If the key is not present + /// in this `fields`, an empty list is returned. However, if the key is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. + get: func(name: field-key) -> list; + + /// Returns `true` when the key is present in this `fields`. If the key is + /// syntactically invalid, `false` is returned. + has: func(name: field-key) -> bool; + + /// Set all of the values for a key. Clears any existing values for that + /// key, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + set: func(name: field-key, value: list) -> result<_, header-error>; + + /// Delete all values for a key. Does nothing if no values for the key + /// exist. + /// + /// Returns any values previously corresponding to the key. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + delete: func(name: field-key) -> result, header-error>; + + /// Append a value for a key. Does not change or delete any existing + /// values for that key. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + append: func(name: field-key, value: field-value) -> result<_, header-error>; + + /// Retrieve the full set of keys and values in the Fields. Like the + /// constructor, the list represents each key-value pair. + /// + /// The outer list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + entries: func() -> list>; + + /// Make a deep copy of the Fields. Equivelant in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + type headers = fields; + + /// Trailers is an alias for Fields. + type trailers = fields; + + /// Represents an HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly empty) + /// set of trailers, indicating that the full contents of the body have been + /// received. This resource represents the contents as a `stream` and the + /// delivery of trailers as a `trailers`, and ensures that the user of this + /// interface may only be consuming either the body contents or waiting on + /// trailers at any given time. + resource body { + + /// Construct a new `body` with the specified stream and trailers. + constructor( + %stream: stream, + trailers: option> + ); + + /// Returns the contents of the body, as a stream of bytes. + /// + /// This function may be called multiple times as long as any `stream`s + /// returned by previous calls have been dropped first. + %stream: func() -> result>; + + /// Takes ownership of `body`, and returns a `trailers`. This function will + /// trap if a `stream` child is still alive. + finish: static func(this: body) -> result, error-code>; + } + + /// Represents an HTTP Request. + resource request { + + /// Construct a new `request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Response. + /// * `body` is the contents of the body, as a stream of bytes. + /// * `trailers` is an optional `future` which resolves to the HTTP Trailers + /// for the Response. + /// * `options` is optional `request-options` to be used if the request is + /// sent over a network connection. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `handler.handle` implementation + /// to reject invalid constructions of `request`. + constructor( + headers: headers, + body: body, + options: option + ); + + /// Get the Method for the Request. + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + set-method: func(method: method) -> result; + + /// Get the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. When + /// `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + set-path-with-query: func(path-with-query: option) -> result; + + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + set-scheme: func(scheme: option) -> result; + + /// Get the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. + authority: func() -> option; + /// Set the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid uri authority. + set-authority: func(authority: option) -> result; + + /// Get the `request-options` to be associated with this request + /// + /// The returned `request-options` resource is immutable: `set-*` operations + /// will fail if invoked. + /// + /// This `request-options` resource is a child: it must be dropped before + /// the parent `request` is dropped, or its ownership is transfered to + /// another component by e.g. `handler.handle`. + options: func() -> option; + + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `request` is dropped, or its ownership is transfered to another + /// component by e.g. `handler.handle`. + headers: func() -> headers; + + /// Get the body associated with the Request. + /// + /// This body resource is a child: it must be dropped before the parent + /// `request` is dropped, or its ownership is transfered to another + /// component by e.g. `handler.handle`. + body: func() -> body; + + /// Takes ownership of the `request` and returns the `headers` and `body`. + into-parts: static func(this: request) -> tuple; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound an + /// asynchronous call. + resource request-options { + /// Construct a default `request-options` value. + constructor(); + + /// The timeout for the initial connect to the HTTP Server. + connect-timeout: func() -> option; + + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported or that this + /// handle is immutable. + set-connect-timeout: func(duration: option) -> result<_, request-options-error>; + + /// The timeout for receiving the first byte of the Response body. + first-byte-timeout: func() -> option; + + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported or that + /// this handle is immutable. + set-first-byte-timeout: func(duration: option) -> result<_, request-options-error>; + + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + between-bytes-timeout: func() -> option; + + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported or that this handle is immutable. + set-between-bytes-timeout: func(duration: option) -> result<_, request-options-error>; + } + + /// This type corresponds to the HTTP standard Status Code. + type status-code = u16; + + /// Represents an HTTP Response. + resource response { + + /// Construct an `response`, with a default `status-code` of `200`. If a + /// different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + /// * `body` is the contents of the body, as a stream of bytes. + /// * `trailers` is an optional `future` which resolves to the HTTP Trailers + /// for the Response. + constructor( + headers: headers, + body: body, + ); + + /// Get the HTTP Status Code for the Response. + status-code: func() -> status-code; + + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + set-status-code: func(status-code: status-code) -> result; + + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `response` is dropped, or its ownership is transfered to another + /// component by e.g. `handler.handle`. + headers: func() -> headers; + + /// Get the body associated with the Response. + /// + /// This body resource is a child: it must be dropped before the parent + /// `response` is dropped, or its ownership is transfered to another + /// component by e.g. `handler.handle`. + body: func() -> body; + + /// Takes ownership of the `response` and returns the `headers` and `body`. + into-parts: static func(this: response) -> tuple; + } +} diff --git a/crates/misc/component-async-tests/wit/test.wit b/crates/misc/component-async-tests/wit/test.wit new file mode 100644 index 000000000000..dff017d777d2 --- /dev/null +++ b/crates/misc/component-async-tests/wit/test.wit @@ -0,0 +1,86 @@ +package local:local; + +interface baz { + foo: func(s: string) -> string; +} + +world round-trip { + import baz; + export baz; +} + +interface ready { + set-ready: func(ready: bool); + when-ready: func(); +} + +interface continue { + set-continue: func(continue: bool); + get-continue: func() -> bool; +} + +interface run { + run: func(); +} + +interface backpressure { + set-backpressure: func(enabled: bool); +} + +interface transmit { + variant control { + read-stream(string), + read-future(string), + write-stream(string), + write-future(string), + } + + exchange: func(control: stream, + caller-stream: stream, + caller-future1: future, + caller-future2: future) -> tuple, future, future>; +} + +world yield-caller { + import continue; + import ready; + import run; + export run; +} + +world yield-callee { + import continue; + export run; +} + +world yield-host { + import continue; + import ready; + export run; +} + +world poll { + import ready; + export run; +} + +world backpressure-caller { + import backpressure; + import run; + export run; +} + +world backpressure-callee { + export backpressure; + export run; +} + +world transmit-caller { + import transmit; + export run; +} + +world transmit-callee { + export transmit; +} + diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index 07d492b298df..d2804990b855 100644 --- a/crates/misc/component-test-util/src/lib.rs +++ b/crates/misc/component-test-util/src/lib.rs @@ -8,15 +8,23 @@ use wasmtime::component::{ComponentNamedList, ComponentType, Func, Lift, Lower, use wasmtime::{AsContextMut, Config, Engine}; pub trait TypedFuncExt { - fn call_and_post_return(&self, store: impl AsContextMut, params: P) -> Result; + fn call_and_post_return( + &self, + store: impl AsContextMut, + params: P, + ) -> Result; } impl TypedFuncExt for TypedFunc where P: ComponentNamedList + Lower, - R: ComponentNamedList + Lift, + R: ComponentNamedList + Lift + Send + Sync + 'static, { - fn call_and_post_return(&self, mut store: impl AsContextMut, params: P) -> Result { + fn call_and_post_return( + &self, + mut store: impl AsContextMut, + params: P, + ) -> Result { let result = self.call(&mut store, params)?; self.post_return(&mut store)?; Ok(result) @@ -24,18 +32,18 @@ where } pub trait FuncExt { - fn call_and_post_return( + fn call_and_post_return( &self, - store: impl AsContextMut, + store: impl AsContextMut, params: &[Val], results: &mut [Val], ) -> Result<()>; } impl FuncExt for Func { - fn call_and_post_return( + fn call_and_post_return( &self, - mut store: impl AsContextMut, + mut store: impl AsContextMut, params: &[Val], results: &mut [Val], ) -> Result<()> { diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index baff3bba8b79..af9ddf13ed6f 100644 --- a/crates/test-programs/Cargo.toml +++ b/crates/test-programs/Cargo.toml @@ -15,9 +15,12 @@ anyhow = { workspace = true, features = ['std'] } wasi = "0.11.0" wasi-nn = "0.6.0" wit-bindgen = { workspace = true, features = ['default'] } +wit-bindgen-rt = { workspace = true, features = ['default'] } libc = { workspace = true } getrandom = "0.2.9" futures = { workspace = true, default-features = false, features = ['alloc'] } url = { workspace = true } sha2 = "0.10.2" base64 = "0.21.0" +once_cell = "1.19.0" +flate2 = "1.0.28" diff --git a/crates/test-programs/artifacts/Cargo.toml b/crates/test-programs/artifacts/Cargo.toml index 40cc3a7bdc91..33a56fbf467e 100644 --- a/crates/test-programs/artifacts/Cargo.toml +++ b/crates/test-programs/artifacts/Cargo.toml @@ -16,4 +16,5 @@ wasmtime = { workspace = true, features = ['incremental-cache', 'cranelift', 'co [build-dependencies] heck = { workspace = true } wit-component = { workspace = true } +wasmparser = { workspace = true, features = ['features'] } cargo_metadata = "0.18.1" diff --git a/crates/test-programs/artifacts/build.rs b/crates/test-programs/artifacts/build.rs index f5966a67ea5e..5c3f3300e2c8 100644 --- a/crates/test-programs/artifacts/build.rs +++ b/crates/test-programs/artifacts/build.rs @@ -4,6 +4,7 @@ use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use wasmparser::{Validator, WasmFeatures}; use wit_component::ComponentEncoder; fn main() { @@ -57,13 +58,13 @@ fn build_and_generate_tests() { let mut kinds = BTreeMap::new(); for target in targets { - let camel = target.to_shouty_snake_case(); + let shouty = target.to_shouty_snake_case(); let wasm = out_dir .join("wasm32-wasip1") .join("debug") .join(format!("{target}.wasm")); - generated_code += &format!("pub const {camel}: &'static str = {wasm:?};\n"); + generated_code += &format!("pub const {shouty}: &'static str = {wasm:?};\n"); // Bucket, based on the name of the test, into a "kind" which generates // a `foreach_*` macro below. @@ -78,6 +79,7 @@ fn build_and_generate_tests() { s if s.starts_with("dwarf_") => "dwarf", s if s.starts_with("config_") => "config", s if s.starts_with("keyvalue_") => "keyvalue", + s if s.starts_with("async_") => "async", // If you're reading this because you hit this panic, either add it // to a test suite above or add a new "suite". The purpose of the // categorization above is to have a static assertion that tests @@ -100,11 +102,12 @@ fn build_and_generate_tests() { } let adapter = match target.as_str() { "reactor" => &reactor_adapter, + s if s.starts_with("async_") => &reactor_adapter, s if s.starts_with("api_proxy") => &proxy_adapter, _ => &command_adapter, }; let path = compile_component(&wasm, adapter); - generated_code += &format!("pub const {camel}_COMPONENT: &'static str = {path:?};\n"); + generated_code += &format!("pub const {shouty}_COMPONENT: &'static str = {path:?};\n"); } for (kind, targets) in kinds { @@ -168,11 +171,18 @@ fn compile_component(wasm: &Path, adapter: &[u8]) -> PathBuf { let component = ComponentEncoder::default() .module(module.as_slice()) .unwrap() - .validate(true) + .validate(false) .adapter("wasi_snapshot_preview1", adapter) .unwrap() .encode() .expect("module can be translated to a component"); + + Validator::new_with_features( + WasmFeatures::WASM2 | WasmFeatures::COMPONENT_MODEL | WasmFeatures::COMPONENT_MODEL_ASYNC, + ) + .validate_all(&component) + .expect("component output should validate"); + let out_dir = wasm.parent().unwrap(); let stem = wasm.file_stem().unwrap().to_str().unwrap(); let component_path = out_dir.join(format!("{stem}.component.wasm")); diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs index bbbbecd1e2da..773efe73d689 100644 --- a/crates/test-programs/src/bin/api_proxy.rs +++ b/crates/test-programs/src/bin/api_proxy.rs @@ -23,7 +23,7 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T { ); assert!(req_hdrs.delete(&header).is_err()); - assert!(req_hdrs.append(&header, &b"no".to_vec()).is_err()); + assert!(req_hdrs.append(&header, b"no".as_ref()).is_err()); assert!( !req_hdrs.has(&header), @@ -31,7 +31,7 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T { ); assert!( - !req_hdrs.has(&"host".to_owned()), + !req_hdrs.has("host"), "forbidden host header present in incoming request" ); diff --git a/crates/test-programs/src/bin/async_backpressure_callee.rs b/crates/test-programs/src/bin/async_backpressure_callee.rs new file mode 100644 index 000000000000..d4f031193e60 --- /dev/null +++ b/crates/test-programs/src/bin/async_backpressure_callee.rs @@ -0,0 +1,36 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "backpressure-callee", + async: { + exports: [ + "local:local/run#run" + ] + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::exports::local::local::{backpressure::Guest as Backpressure, run::Guest as Run}, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Run for Component { + async fn run() { + // do nothing + } +} + +impl Backpressure for Component { + fn set_backpressure(enabled: bool) { + async_support::task_backpressure(enabled); + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_backpressure_caller.rs b/crates/test-programs/src/bin/async_backpressure_caller.rs new file mode 100644 index 000000000000..7ef6478be295 --- /dev/null +++ b/crates/test-programs/src/bin/async_backpressure_caller.rs @@ -0,0 +1,81 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "backpressure-caller", + async: { + imports: [ + "local:local/run#run" + ], + exports: [ + "local:local/run#run" + ] + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::local::local::run::Guest, + local::local::{backpressure, run}, + }, + futures::future, + std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, + }, +}; + +struct Component; + +impl Guest for Component { + async fn run() { + backpressure::set_backpressure(true); + + let mut a = Some(Box::pin(run::run())); + let mut b = Some(Box::pin(run::run())); + let mut c = Some(Box::pin(run::run())); + + let mut backpressure_is_set = true; + future::poll_fn(move |cx| { + let a_ready = is_ready(cx, &mut a); + let b_ready = is_ready(cx, &mut b); + let c_ready = is_ready(cx, &mut c); + + if backpressure_is_set { + assert!(!a_ready); + assert!(!b_ready); + assert!(!c_ready); + + backpressure::set_backpressure(false); + backpressure_is_set = false; + + Poll::Pending + } else if a_ready && b_ready && c_ready { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } +} + +fn is_ready(cx: &mut Context, fut: &mut Option>>>) -> bool { + if let Some(v) = fut.as_mut() { + if v.as_mut().poll(cx).is_ready() { + *fut = None; + true + } else { + false + } + } else { + true + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_http_echo.rs b/crates/test-programs/src/bin/async_http_echo.rs new file mode 100644 index 000000000000..5c814e06fa18 --- /dev/null +++ b/crates/test-programs/src/bin/async_http_echo.rs @@ -0,0 +1,68 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "wasi:http/proxy", + async: { + imports: [ + "wasi:http/types@0.3.0-draft#[static]body.finish", + "wasi:http/handler@0.3.0-draft#handle", + ], + exports: [ + "wasi:http/handler@0.3.0-draft#handle", + ] + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::wasi::http::handler::Guest as Handler, + stream_and_future_support, + wasi::http::types::{Body, ErrorCode, Request, Response}, + }, + futures::{SinkExt, StreamExt}, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Handler for Component { + /// Return a response which echoes the request headers, body, and trailers. + async fn handle(request: Request) -> Result { + let (headers, body) = Request::into_parts(request); + + if false { + // This is the easy and efficient way to do it... + Ok(Response::new(headers, body)) + } else { + // ...but we do it the more difficult, less efficient way here to exercise various component model + // features (e.g. `future`s, `stream`s, and post-return asynchronous execution): + let (trailers_tx, trailers_rx) = stream_and_future_support::new_future(); + let (mut pipe_tx, pipe_rx) = stream_and_future_support::new_stream(); + + async_support::spawn(async move { + let mut body_rx = body.stream().unwrap(); + while let Some(chunk) = body_rx.next().await { + pipe_tx.send(chunk).await.unwrap(); + } + + drop(pipe_tx); + + if let Some(trailers) = Body::finish(body).await.unwrap() { + trailers_tx.write(trailers).await; + } + }); + + Ok(Response::new( + headers, + Body::new(pipe_rx, Some(trailers_rx)), + )) + } + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_http_middleware.rs b/crates/test-programs/src/bin/async_http_middleware.rs new file mode 100644 index 000000000000..4c4b01e1093f --- /dev/null +++ b/crates/test-programs/src/bin/async_http_middleware.rs @@ -0,0 +1,161 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "wasi:http/proxy", + async: { + imports: [ + "wasi:http/types@0.3.0-draft#[static]body.finish", + "wasi:http/handler@0.3.0-draft#handle", + ], + exports: [ + "wasi:http/handler@0.3.0-draft#handle", + ] + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::wasi::http::handler::Guest as Handler, + stream_and_future_support, + wasi::http::{ + handler, + types::{Body, ErrorCode, Headers, Request, Response}, + }, + }, + flate2::{ + write::{DeflateDecoder, DeflateEncoder}, + Compression, + }, + futures::{SinkExt, StreamExt}, + std::{io::Write, mem}, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Handler for Component { + /// Forward the specified request to the imported `wasi:http/handler`, transparently decoding the request body + /// if it is `deflate`d and then encoding the response body if the client has provided an `accept-encoding: + /// deflate` header. + async fn handle(request: Request) -> Result { + // First, extract the parts of the request and check for (and remove) headers pertaining to body encodings. + let method = request.method(); + let scheme = request.scheme(); + let path_with_query = request.path_with_query(); + let authority = request.authority(); + let mut accept_deflated = false; + let mut content_deflated = false; + let (headers, body) = Request::into_parts(request); + let mut headers = headers.entries(); + headers.retain(|(k, v)| match (k.as_str(), v.as_slice()) { + ("accept-encoding", b"deflate") => { + accept_deflated = true; + false + } + ("content-encoding", b"deflate") => { + content_deflated = true; + false + } + _ => true, + }); + + let body = if content_deflated { + // Next, spawn a task to pipe and decode the original request body and trailers into a new request + // we'll create below. This will run concurrently with any code in the imported `wasi:http/handler`. + let (trailers_tx, trailers_rx) = stream_and_future_support::new_future(); + let (mut pipe_tx, pipe_rx) = stream_and_future_support::new_stream(); + + async_support::spawn(async move { + { + let mut body_rx = body.stream().unwrap(); + + let mut decoder = DeflateDecoder::new(Vec::new()); + + while let Some(chunk) = body_rx.next().await { + decoder.write_all(&chunk).unwrap(); + pipe_tx.send(mem::take(decoder.get_mut())).await.unwrap(); + } + + pipe_tx.send(decoder.finish().unwrap()).await.unwrap(); + + drop(pipe_tx); + } + + if let Some(trailers) = Body::finish(body).await.unwrap() { + trailers_tx.write(trailers).await; + } + }); + + Body::new(pipe_rx, Some(trailers_rx)) + } else { + body + }; + + // While the above task (if any) is running, synthesize a request from the parts collected above and pass + // it to the imported `wasi:http/handler`. + let my_request = Request::new(Headers::from_list(&headers).unwrap(), body, None); + my_request.set_method(&method).unwrap(); + my_request.set_scheme(scheme.as_ref()).unwrap(); + my_request + .set_path_with_query(path_with_query.as_deref()) + .unwrap(); + my_request.set_authority(authority.as_deref()).unwrap(); + + let response = handler::handle(my_request).await?; + + // Now that we have the response, extract the parts, adding an extra header if we'll be encoding the body. + let status_code = response.status_code(); + let (headers, body) = Response::into_parts(response); + let mut headers = headers.entries(); + if accept_deflated { + headers.push(("content-encoding".into(), b"deflate".into())); + } + + let body = if accept_deflated { + // Spawn another task; this one is to pipe and encode the original response body and trailers into a + // new response we'll create below. This will run concurrently with the caller's code (i.e. it won't + // necessarily complete before we return a value). + let (trailers_tx, trailers_rx) = stream_and_future_support::new_future(); + let (mut pipe_tx, pipe_rx) = stream_and_future_support::new_stream(); + + async_support::spawn(async move { + { + let mut body_rx = body.stream().unwrap(); + + let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast()); + + while let Some(chunk) = body_rx.next().await { + encoder.write_all(&chunk).unwrap(); + pipe_tx.send(mem::take(encoder.get_mut())).await.unwrap(); + } + + pipe_tx.send(encoder.finish().unwrap()).await.unwrap(); + + drop(pipe_tx); + } + + if let Some(trailers) = Body::finish(body).await.unwrap() { + trailers_tx.write(trailers).await; + } + }); + + Body::new(pipe_rx, Some(trailers_rx)) + } else { + body + }; + + // While the above tasks (if any) are running, synthesize a response from the parts collected above and + // return it. + let my_response = Response::new(Headers::from_list(&headers).unwrap(), body); + my_response.set_status_code(status_code).unwrap(); + + Ok(my_response) + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_poll.rs b/crates/test-programs/src/bin/async_poll.rs new file mode 100644 index 000000000000..60973a2c6223 --- /dev/null +++ b/crates/test-programs/src/bin/async_poll.rs @@ -0,0 +1,102 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "poll", + }); + + use super::Component; + export!(Component); +} + +use bindings::{exports::local::local::run::Guest, local::local::ready}; + +fn task_poll() -> Option<(i32, i32, i32)> { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-poll]"] + fn poll(_: *mut i32) -> i32; + } + let mut payload = [0i32; 3]; + if unsafe { poll(payload.as_mut_ptr()) } != 0 { + Some((payload[0], payload[1], payload[2])) + } else { + None + } + } +} + +fn async_when_ready() -> i32 { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!() + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "local:local/ready")] + extern "C" { + #[link_name = "[async]when-ready"] + fn call_when_ready(_: *mut u8, _: *mut u8) -> i32; + } + unsafe { call_when_ready(std::ptr::null_mut(), std::ptr::null_mut()) } + } +} + +/// Call the `subtask.drop` canonical built-in function. +fn subtask_drop(subtask: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = subtask; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(_: u32); + } + unsafe { + subtask_drop(subtask); + } + } +} + +struct Component; + +impl Guest for Component { + fn run() { + ready::set_ready(false); + + assert!(task_poll().is_none()); + + async_when_ready(); + + assert!(task_poll().is_none()); + + ready::set_ready(true); + + let Some((3, task, _)) = task_poll() else { + panic!() + }; + + subtask_drop(task as u32); + + assert!(task_poll().is_none()); + + assert!(async_when_ready() == 3 << 30); // STATUS_DONE + + assert!(task_poll().is_none()); + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_round_trip_stackful.rs b/crates/test-programs/src/bin/async_round_trip_stackful.rs new file mode 100644 index 000000000000..ae367a729899 --- /dev/null +++ b/crates/test-programs/src/bin/async_round_trip_stackful.rs @@ -0,0 +1,150 @@ +// This tests callback-less (AKA stackful) async exports. +// +// Testing this case using Rust's LLVM-based toolchain is tricky because, as of +// this writing, LLVM does not produce reentrance-safe code. Specifically, it +// allocates a single shadow stack for use whenever a program needs to take the +// address of a stack variable, which makes concurrent execution of multiple +// Wasm stacks in the same instance hazardous. +// +// Given the above, we write code directly against the component model ABI +// rather than use `wit-bindgen`, and we carefully avoid use of the shadow stack +// across yield points such as calls to `task.wait` in order to keep the code +// reentrant. + +use std::alloc::{self, Layout}; + +#[cfg(target_arch = "wasm32")] +#[link(wasm_import_module = "[export]local:local/baz")] +extern "C" { + #[link_name = "[task-return]foo"] + fn task_return_foo(ptr: *mut u8, len: usize); +} +#[cfg(not(target_arch = "wasm32"))] +extern "C" fn task_return_foo(_ptr: *mut u8, _len: usize) { + unreachable!() +} + +#[cfg(target_arch = "wasm32")] +#[link(wasm_import_module = "local:local/baz")] +extern "C" { + #[link_name = "[async]foo"] + fn import_foo(params: *mut u8, results: *mut u8) -> u32; +} +#[cfg(not(target_arch = "wasm32"))] +extern "C" fn import_foo(_params: *mut u8, _results: *mut u8) -> u32 { + unreachable!() +} + +#[cfg(target_arch = "wasm32")] +#[link(wasm_import_module = "$root")] +extern "C" { + #[link_name = "[task-wait]"] + fn task_wait(results: *mut i32) -> i32; +} +#[cfg(not(target_arch = "wasm32"))] +extern "C" fn task_wait(_results: *mut i32) -> i32 { + unreachable!() +} + +#[cfg(target_arch = "wasm32")] +#[link(wasm_import_module = "$root")] +extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(task: u32); +} +#[cfg(not(target_arch = "wasm32"))] +extern "C" fn subtask_drop(_task: u32) { + unreachable!() +} + +const _STATUS_STARTING: u32 = 0; +const _STATUS_STARTED: u32 = 1; +const _STATUS_RETURNED: u32 = 2; +const STATUS_DONE: u32 = 3; + +const _EVENT_CALL_STARTING: i32 = 0; +const _EVENT_CALL_STARTED: i32 = 1; +const _EVENT_CALL_RETURNED: i32 = 2; +const EVENT_CALL_DONE: i32 = 3; + +#[export_name = "[async-stackful]local:local/baz#foo"] +unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) { + // Note that we're careful not to take the address of any stack-allocated + // value here. We need to avoid relying on the LLVM-generated shadow stack + // in order to correctly support reentrancy. It's okay to call functions + // which use the shadow stack, as long as they pop everything off before we + // reach a yield point such as a call to `task.wait`. + + let s = format!( + "{} - entered guest", + String::from_utf8(Vec::from_raw_parts(ptr, len, len)).unwrap() + ); + + let layout = Layout::from_size_align(8, 4).unwrap(); + + let params = alloc::alloc(layout); + *params.cast::<*mut u8>() = s.as_ptr().cast_mut(); + *params.add(4).cast::() = s.len(); + + let results = alloc::alloc(layout); + + let result = import_foo(params, results); + let mut status = result >> 30; + let call = result & !(0b11 << 30); + while status != STATUS_DONE { + // Note the use of `Box` here to avoid taking the address of a stack + // allocation. + let payload = Box::into_raw(Box::new([0i32; 2])); + let event = task_wait(payload.cast()); + let payload = Box::from_raw(payload); + if event == EVENT_CALL_DONE { + assert!(call == payload[0] as u32); + subtask_drop(call); + status = STATUS_DONE; + } + } + alloc::dealloc(params, layout); + + let len = *results.add(4).cast::(); + let s = format!( + "{} - exited guest", + String::from_utf8(Vec::from_raw_parts(*results.cast::<*mut u8>(), len, len)).unwrap() + ); + alloc::dealloc(results, layout); + + task_return_foo(s.as_ptr().cast_mut(), s.len()); +} + +// Copied from `wit-bindgen`-generated output +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.35.0:local:local:round-trip:encoded world"] +#[doc(hidden)] +#[allow( + clippy::octal_escapes, + reason = "this is a machine-generated binary blob" +)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 239] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07o\x01A\x02\x01A\x04\x01\ +B\x02\x01@\x01\x01ss\0s\x04\0\x03foo\x01\0\x03\0\x0flocal:local/baz\x05\0\x01B\x02\ +\x01@\x01\x01ss\0s\x04\0\x03foo\x01\0\x04\0\x0flocal:local/baz\x05\x01\x04\0\x16\ +local:local/round-trip\x04\0\x0b\x10\x01\0\x0around-trip\x03\0\0\0G\x09producers\ +\x01\x0cprocessed-by\x02\x0dwit-component\x070.220.0\x10wit-bindgen-rust\x060.35\ +.0"; + +/// # Safety +/// TODO +#[export_name = "cabi_realloc"] +pub unsafe extern "C" fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_size: usize, +) -> *mut u8 { + assert!(old_ptr.is_null()); + assert!(old_len == 0); + + alloc::alloc(Layout::from_size_align(new_size, align).unwrap()) +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_round_trip_stackless.rs b/crates/test-programs/src/bin/async_round_trip_stackless.rs new file mode 100644 index 000000000000..f06bf95571c2 --- /dev/null +++ b/crates/test-programs/src/bin/async_round_trip_stackless.rs @@ -0,0 +1,26 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "round-trip", + async: true, + }); + + use super::Component; + export!(Component); +} + +use bindings::{exports::local::local::baz::Guest as Baz, local::local::baz}; + +struct Component; + +impl Baz for Component { + async fn foo(s: String) -> String { + format!( + "{} - exited guest", + baz::foo(&format!("{s} - entered guest")).await + ) + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_round_trip_synchronous.rs b/crates/test-programs/src/bin/async_round_trip_synchronous.rs new file mode 100644 index 000000000000..bcf4ccae2104 --- /dev/null +++ b/crates/test-programs/src/bin/async_round_trip_synchronous.rs @@ -0,0 +1,25 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "round-trip", + }); + + use super::Component; + export!(Component); +} + +use bindings::{exports::local::local::baz::Guest as Baz, local::local::baz}; + +struct Component; + +impl Baz for Component { + fn foo(s: String) -> String { + format!( + "{} - exited guest", + baz::foo(&format!("{s} - entered guest")) + ) + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_round_trip_wait.rs b/crates/test-programs/src/bin/async_round_trip_wait.rs new file mode 100644 index 000000000000..6f3b3ced7fc4 --- /dev/null +++ b/crates/test-programs/src/bin/async_round_trip_wait.rs @@ -0,0 +1,35 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "round-trip", + async: { + imports: [ + "local:local/baz#foo", + ] + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{exports::local::local::baz::Guest as Baz, local::local::baz}, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Baz for Component { + fn foo(s: String) -> String { + async_support::block_on(async move { + format!( + "{} - exited guest", + baz::foo(&format!("{s} - entered guest")).await + ) + }) + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_transmit_callee.rs b/crates/test-programs/src/bin/async_transmit_callee.rs new file mode 100644 index 000000000000..05f80cfe6f98 --- /dev/null +++ b/crates/test-programs/src/bin/async_transmit_callee.rs @@ -0,0 +1,77 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "transmit-callee", + async: { + exports: [ + "local:local/transmit#exchange", + ], + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::local::local::transmit::{Control, Guest}, + stream_and_future_support::{self, FutureReader, StreamReader}, + }, + futures::{SinkExt, StreamExt}, + std::future::IntoFuture, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Guest for Component { + async fn exchange( + mut control_rx: StreamReader, + mut caller_stream_rx: StreamReader, + caller_future_rx1: FutureReader, + caller_future_rx2: FutureReader, + ) -> ( + StreamReader, + FutureReader, + FutureReader, + ) { + let (mut callee_stream_tx, callee_stream_rx) = stream_and_future_support::new_stream(); + let (callee_future_tx1, callee_future_rx1) = stream_and_future_support::new_future(); + let (callee_future_tx2, callee_future_rx2) = stream_and_future_support::new_future(); + + async_support::spawn(async move { + let mut caller_future_rx1 = Some(caller_future_rx1); + let mut callee_future_tx1 = Some(callee_future_tx1); + + while let Some(messages) = control_rx.next().await { + for message in messages { + match message { + Control::ReadStream(value) => { + assert_eq!(caller_stream_rx.next().await, Some(vec![value])); + } + Control::ReadFuture(value) => { + assert_eq!( + caller_future_rx1.take().unwrap().into_future().await, + Some(value) + ); + } + Control::WriteStream(value) => { + callee_stream_tx.send(vec![value]).await.unwrap(); + } + Control::WriteFuture(value) => { + callee_future_tx1.take().unwrap().write(value).await; + } + } + } + } + + drop((caller_future_rx2, callee_future_tx2)); + }); + + (callee_stream_rx, callee_future_rx1, callee_future_rx2) + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_transmit_caller.rs b/crates/test-programs/src/bin/async_transmit_caller.rs new file mode 100644 index 000000000000..84310a9df20e --- /dev/null +++ b/crates/test-programs/src/bin/async_transmit_caller.rs @@ -0,0 +1,166 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "transmit-caller", + async: { + imports: [ + "local:local/transmit#exchange", + ], + exports: [ + "local:local/run#run", + ], + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::local::local::run::Guest, + local::local::transmit::{self, Control}, + stream_and_future_support, + }, + futures::{future, FutureExt, SinkExt, StreamExt}, + std::{ + future::{Future, IntoFuture}, + pin::pin, + task::Poll, + }, +}; + +struct Component; + +impl Guest for Component { + async fn run() { + let (mut control_tx, control_rx) = stream_and_future_support::new_stream(); + let (mut caller_stream_tx, caller_stream_rx) = stream_and_future_support::new_stream(); + let (mut caller_future_tx1, caller_future_rx1) = stream_and_future_support::new_future(); + let (caller_future_tx2, caller_future_rx2) = stream_and_future_support::new_future(); + + let (mut callee_stream_rx, mut callee_future_rx1, callee_future_rx2) = transmit::exchange( + control_rx, + caller_stream_rx, + caller_future_rx1, + caller_future_rx2, + ) + .await; + + // Tell peer to read from its end of the stream and assert that the result matches an expected value. + control_tx + .send(vec![Control::ReadStream("a".into())]) + .await + .unwrap(); + caller_stream_tx.send(vec!["a".into()]).await.unwrap(); + + // Start writing another value, but cancel the write before telling the peer to read. + { + let send = caller_stream_tx.send(vec!["b".into()]); + assert!(poll(send).await.is_err()); + caller_stream_tx.cancel(); + } + + // Tell the peer to read an expected value again, which should _not_ match the value provided in the + // canceled write above. + control_tx + .send(vec![Control::ReadStream("c".into())]) + .await + .unwrap(); + caller_stream_tx.send(vec!["c".into()]).await.unwrap(); + + // Start writing a value to the future, but cancel the write before telling the peer to read. + { + let send = caller_future_tx1.write("x".into()); + match poll(send).await { + Ok(_) => panic!(), + Err(send) => caller_future_tx1 = send.cancel(), + } + } + + // Tell the peer to read an expected value again, which should _not_ match the value provided in the + // canceled write above. + control_tx + .send(vec![Control::ReadFuture("y".into())]) + .await + .unwrap(); + caller_future_tx1.write("y".into()).await; + + // Tell the peer to write a value to its end of the stream, then read from our end and assert the value + // matches. + control_tx + .send(vec![Control::WriteStream("a".into())]) + .await + .unwrap(); + assert_eq!(callee_stream_rx.next().await, Some(vec!["a".into()])); + + // Start reading a value from the stream, but cancel the read before telling the peer to write. + { + let next = callee_stream_rx.next(); + assert!(poll(next).await.is_err()); + callee_stream_rx.cancel(); + } + + // Once again, tell the peer to write a value to its end of the stream, then read from our end and assert + // the value matches. + control_tx + .send(vec![Control::WriteStream("b".into())]) + .await + .unwrap(); + assert_eq!(callee_stream_rx.next().await, Some(vec!["b".into()])); + + // Start reading a value from the future, but cancel the read before telling the peer to write. + { + let next = callee_future_rx1.into_future(); + match poll(next).await { + Ok(_) => panic!(), + Err(next) => callee_future_rx1 = next.cancel(), + } + } + + // Tell the peer to write a value to its end of the future, then read from our end and assert the value + // matches. + control_tx + .send(vec![Control::WriteFuture("b".into())]) + .await + .unwrap(); + assert_eq!(callee_future_rx1.into_future().await, Some("b".into())); + + // Start writing a value to the stream, but drop the stream without telling the peer to read. + let send = caller_stream_tx.send(vec!["d".into()]); + assert!(poll(send).await.is_err()); + drop(caller_stream_tx); + + // Start reading a value from the stream, but drop the stream without telling the peer to write. + let next = callee_stream_rx.next(); + assert!(poll(next).await.is_err()); + drop(callee_stream_rx); + + // Start writing a value to the future, but drop the write without telling the peer to read. + { + let send = pin!(caller_future_tx2.write("x".into())); + assert!(poll(send).await.is_err()); + } + + // Start reading a value from the future, but drop the read without telling the peer to write. + { + let next = callee_future_rx2.into_future(); + assert!(poll(next).await.is_err()); + } + } +} + +async fn poll + Unpin>(fut: F) -> Result { + let mut fut = Some(fut); + future::poll_fn(move |cx| { + let mut fut = fut.take().unwrap(); + Poll::Ready(match fut.poll_unpin(cx) { + Poll::Ready(v) => Ok(v), + Poll::Pending => Err(fut), + }) + }) + .await +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_yield_callee.rs b/crates/test-programs/src/bin/async_yield_callee.rs new file mode 100644 index 000000000000..4274546ce3dd --- /dev/null +++ b/crates/test-programs/src/bin/async_yield_callee.rs @@ -0,0 +1,27 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "yield-callee", + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{exports::local::local::run::Guest, local::local::continue_}, + wit_bindgen_rt::async_support, +}; + +struct Component; + +impl Guest for Component { + fn run() { + while continue_::get_continue() { + async_support::task_yield(); + } + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/async_yield_caller.rs b/crates/test-programs/src/bin/async_yield_caller.rs new file mode 100644 index 000000000000..3cdd13ade127 --- /dev/null +++ b/crates/test-programs/src/bin/async_yield_caller.rs @@ -0,0 +1,62 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "yield-caller", + async: { + imports: [ + "local:local/ready#when-ready", + "local:local/run#run", + ], + exports: [ + "local:local/run#run", + ], + } + }); + + use super::Component; + export!(Component); +} + +use { + bindings::{ + exports::local::local::run::Guest, + local::local::{continue_, ready, run}, + }, + futures::future, + std::{future::Future, task::Poll}, +}; + +struct Component; + +impl Guest for Component { + async fn run() { + ready::set_ready(false); + continue_::set_continue(true); + + let mut ready = Some(Box::pin(ready::when_ready())); + let mut run = Some(Box::pin(run::run())); + future::poll_fn(move |cx| { + let ready_poll = ready.as_mut().map(|v| v.as_mut().poll(cx)); + ready::set_ready(true); + let run_poll = run.as_mut().map(|v| v.as_mut().poll(cx)); + + match (run_poll, ready_poll) { + (None | Some(Poll::Ready(())), None | Some(Poll::Ready(()))) => { + return Poll::Ready(()); + } + (Some(Poll::Ready(())), _) => run = None, + (_, Some(Poll::Ready(()))) => { + ready = None; + continue_::set_continue(false); + } + _ => {} + } + + Poll::Pending + }) + .await + } +} + +// Unused function; required since this file is built as a `bin`: +fn main() {} diff --git a/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs index 1f532ee25e2c..d1e2a93457e2 100644 --- a/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs +++ b/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs @@ -3,48 +3,42 @@ use test_programs::wasi::http::types::{HeaderError, Headers, OutgoingRequest}; fn main() { let hdrs = Headers::new(); assert!(matches!( - hdrs.append(&"malformed header name".to_owned(), &b"ok value".to_vec()), + hdrs.append("malformed header name", b"ok value".as_ref()), Err(HeaderError::InvalidSyntax) )); assert!(matches!( - hdrs.append(&"ok-header-name".to_owned(), &b"ok value".to_vec()), + hdrs.append("ok-header-name", b"ok value".as_ref()), Ok(()) )); assert!(matches!( - hdrs.append(&"ok-header-name".to_owned(), &b"bad\nvalue".to_vec()), + hdrs.append("ok-header-name", b"bad\nvalue".as_ref()), Err(HeaderError::InvalidSyntax) )); assert!(matches!( - hdrs.append(&"Connection".to_owned(), &b"keep-alive".to_vec()), + hdrs.append("Connection", b"keep-alive".as_ref()), Err(HeaderError::Forbidden) )); assert!(matches!( - hdrs.append(&"Keep-Alive".to_owned(), &b"stuff".to_vec()), + hdrs.append("Keep-Alive", b"stuff".as_ref()), Err(HeaderError::Forbidden) )); assert!(matches!( - hdrs.append(&"Host".to_owned(), &b"example.com".to_vec()), + hdrs.append("Host", b"example.com".as_ref()), Err(HeaderError::Forbidden) )); assert!(matches!( - hdrs.append( - &"custom-forbidden-header".to_owned(), - &b"keep-alive".to_vec() - ), + hdrs.append("custom-forbidden-header", b"keep-alive".as_ref()), Err(HeaderError::Forbidden) )); assert!(matches!( - hdrs.append( - &"Custom-Forbidden-Header".to_owned(), - &b"keep-alive".to_vec() - ), + hdrs.append("Custom-Forbidden-Header", b"keep-alive".as_ref()), Err(HeaderError::Forbidden) )); @@ -67,17 +61,17 @@ fn main() { let hdrs = req.headers(); assert!(matches!( - hdrs.set(&"Content-Length".to_owned(), &[b"10".to_vec()]), + hdrs.set("Content-Length", &[b"10".to_vec()]), Err(HeaderError::Immutable), )); assert!(matches!( - hdrs.append(&"Content-Length".to_owned(), &b"10".to_vec()), + hdrs.append("Content-Length", b"10".as_ref()), Err(HeaderError::Immutable), )); assert!(matches!( - hdrs.delete(&"Content-Length".to_owned()), + hdrs.delete("Content-Length"), Err(HeaderError::Immutable), )); } diff --git a/crates/wasi-config/Cargo.toml b/crates/wasi-config/Cargo.toml index 81bd61ef7184..cadaf77bb7a2 100644 --- a/crates/wasi-config/Cargo.toml +++ b/crates/wasi-config/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [dependencies] anyhow = { workspace = true } -wasmtime = { workspace = true, features = ["runtime", "component-model"] } +wasmtime = { workspace = true, features = ["runtime", "component-model", "async"] } [dev-dependencies] test-programs-artifacts = { workspace = true } diff --git a/crates/wasi-keyvalue/src/lib.rs b/crates/wasi-keyvalue/src/lib.rs index 4d53a9acf20b..d88b5220aeec 100644 --- a/crates/wasi-keyvalue/src/lib.rs +++ b/crates/wasi-keyvalue/src/lib.rs @@ -75,7 +75,7 @@ mod generated { }, trappable_error_type: { "wasi:keyvalue/store/error" => crate::Error, - }, + } }); } diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index ef2db1f2ca69..11bea875c05c 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -62,6 +62,7 @@ semver = { workspace = true, optional = true } smallvec = { workspace = true, optional = true } hashbrown = { workspace = true, features = ["ahash"] } bitflags = { workspace = true } +futures = { workspace = true, features = ["alloc"] } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] workspace = true @@ -368,3 +369,12 @@ wave = ["dep:wasm-wave"] signals-based-traps = [ "dep:wasmtime-jit-icache-coherence", ] + +# Enables support for the Component Model Async ABI, along with `future`, +# `stream`, and `error-context` types. +component-model-async = [ + "async", + "component-model", + "std", + 'wasmtime-component-macro?/component-model-async', +] diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 31de2c06803f..eb3c780342a1 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1059,6 +1059,17 @@ impl Config { self } + /// Configures whether components support the async ABI [proposal] for + /// lifting and lowering functions, as well as `stream`, `future`, and + /// `error-context` types. + /// + /// [proposal]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md + #[cfg(feature = "component-model-async")] + pub fn wasm_component_model_async(&mut self, enable: bool) -> &mut Self { + self.wasm_feature(WasmFeatures::COMPONENT_MODEL_ASYNC, enable); + self + } + /// Configures which compilation strategy will be used for wasm modules. /// /// This method can be used to configure which compiler is used for wasm diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 84a1b36bb628..8051970917c6 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -202,6 +202,7 @@ struct WasmFeatures { custom_page_sizes: bool, component_model_more_flags: bool, component_model_multiple_returns: bool, + component_model_async: bool, gc_types: bool, wide_arithmetic: bool, } @@ -231,6 +232,7 @@ impl Metadata<'_> { component_model_nested_names, component_model_more_flags, component_model_multiple_returns, + component_model_async, legacy_exceptions, gc_types, stack_switching, @@ -276,6 +278,7 @@ impl Metadata<'_> { custom_page_sizes, component_model_more_flags, component_model_multiple_returns, + component_model_async, gc_types, wide_arithmetic, }, @@ -486,6 +489,7 @@ impl Metadata<'_> { custom_page_sizes, component_model_more_flags, component_model_multiple_returns, + component_model_async, gc_types, wide_arithmetic, } = self.features; @@ -572,6 +576,11 @@ impl Metadata<'_> { other.contains(F::COMPONENT_MODEL_MULTIPLE_RETURNS), "WebAssembly component model support for multiple returns", )?; + Self::check_bool( + component_model_async, + other.contains(F::COMPONENT_MODEL_ASYNC), + "WebAssembly component model support for async lifts/lowers, futures, streams, and errors", + )?; Self::check_cfg_bool( cfg!(feature = "gc"), "gc", diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs index 10af60dc94ec..e0a7afb3a626 100644 --- a/crates/wasmtime/src/runtime/component/component.rs +++ b/crates/wasmtime/src/runtime/component/component.rs @@ -602,6 +602,7 @@ impl Component { GlobalInitializer::LowerImport { .. } | GlobalInitializer::ExtractMemory(_) | GlobalInitializer::ExtractRealloc(_) + | GlobalInitializer::ExtractCallback(_) | GlobalInitializer::ExtractPostReturn(_) | GlobalInitializer::Resource(_) => {} } diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs new file mode 100644 index 000000000000..c33806f57681 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -0,0 +1,2085 @@ +use { + crate::{ + component::func::{self, Func, Lower as _, LowerContext, Options}, + vm::{ + component::{ComponentInstance, VMComponentContext, WaitableState}, + mpk::{self, ProtectionMask}, + AsyncWasmCallState, PreviousAsyncWasmCallState, SendSyncPtr, VMFuncRef, + VMMemoryDefinition, VMOpaqueContext, VMStore, + }, + AsContextMut, CallHook, Engine, StoreContextMut, ValRaw, + }, + anyhow::{anyhow, bail, Context as _, Result}, + futures::{ + channel::oneshot, + future::{self, Either, FutureExt}, + stream::{FuturesUnordered, StreamExt}, + }, + once_cell::sync::Lazy, + ready_chunks::ReadyChunks, + std::{ + any::Any, + borrow::ToOwned, + boxed::Box, + cell::UnsafeCell, + collections::{HashMap, HashSet, VecDeque}, + future::Future, + marker::PhantomData, + mem::{self, MaybeUninit}, + pin::{pin, Pin}, + ptr::{self, NonNull}, + sync::{Arc, Mutex}, + task::{Context, Poll, Wake, Waker}, + vec::Vec, + }, + table::{Table, TableId}, + wasmtime_environ::component::{ + InterfaceType, RuntimeComponentInstanceIndex, StringEncoding, TypeTaskReturnIndex, + MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + }, + wasmtime_fiber::{Fiber, Suspend}, +}; + +use futures_and_streams::TransmitState; +pub(crate) use futures_and_streams::{ + error_context_debug_message, error_context_drop, error_context_new, flat_stream_read, + flat_stream_write, future_cancel_read, future_cancel_write, future_close_readable, + future_close_writable, future_new, future_read, future_write, stream_cancel_read, + stream_cancel_write, stream_close_readable, stream_close_writable, stream_new, stream_read, + stream_write, +}; +pub use futures_and_streams::{ + future, stream, ErrorContext, FutureReader, FutureWriter, StreamReader, StreamWriter, +}; + +mod futures_and_streams; +mod ready_chunks; +mod table; + +// TODO: The handling of `task.yield` and `task.backpressure` was bolted on late in the implementation and is +// currently haphazard. We need a refactor to manage yielding, backpressure, and event polling and delivery in a +// more unified and structured way. + +// TODO: move these into an enum: +const STATUS_STARTING: u32 = 0; +const STATUS_STARTED: u32 = 1; +const STATUS_RETURNED: u32 = 2; +const STATUS_DONE: u32 = 3; + +mod events { + // TODO: move these into an enum: + pub const _EVENT_CALL_STARTING: u32 = 0; + pub const EVENT_CALL_STARTED: u32 = 1; + pub const EVENT_CALL_RETURNED: u32 = 2; + pub const EVENT_CALL_DONE: u32 = 3; + pub const _EVENT_YIELDED: u32 = 4; + pub const EVENT_STREAM_READ: u32 = 5; + pub const EVENT_STREAM_WRITE: u32 = 6; + pub const EVENT_FUTURE_READ: u32 = 7; + pub const EVENT_FUTURE_WRITE: u32 = 8; +} + +const EXIT_FLAG_ASYNC_CALLER: u32 = 1 << 0; +const EXIT_FLAG_ASYNC_CALLEE: u32 = 1 << 1; + +/// Represents the result of a concurrent operation. +/// +/// This is similar to a [`std::future::Future`] except that it represents an +/// operation which requires exclusive access to a store in order to make +/// progress -- without monopolizing that store for the lifetime of the +/// operation. +pub struct Promise(Pin + Send + Sync + 'static>>); + +impl Promise { + /// Map the result of this `Promise` from one value to another. + pub fn map(self, fun: impl FnOnce(T) -> U + Send + Sync + 'static) -> Promise { + Promise(Box::pin(self.0.map(fun))) + } + + /// Convert this `Promise` to a future which may be `await`ed for its + /// result. + /// + /// The returned future will require exclusive use of the store until it + /// completes. If you need to await more than one `Promise` concurrently, + /// use [`PromisesUnordered`]. + pub async fn get(self, mut store: impl AsContextMut) -> Result { + Ok(poll_until(store.as_context_mut(), self.0).await?.1) + } + + /// Convert this `Promise` to a future which may be `await`ed for its + /// result. + /// + /// Unlike [`Self::get`], this does _not_ take a store parameter, meaning + /// the returned future will not make progress until and unless the event + /// loop for the store it came from is polled. Thus, this method should + /// only be used from within host functions and not from top-level embedder + /// code. + pub fn into_future(self) -> Pin + Send + Sync + 'static>> { + self.0 + } +} + +/// Represents a collection of zero or more concurrent operations. +/// +/// Similar to [`futures::stream::FuturesUnordered`], this type supports +/// `await`ing more than one [`Promise`]s concurrently. +pub struct PromisesUnordered( + FuturesUnordered + Send + Sync + 'static>>>, +); + +impl PromisesUnordered { + /// Create a new `PromisesUnordered` with no entries. + pub fn new() -> Self { + Self(FuturesUnordered::new()) + } + + /// Add the specified [`Promise`] to this collection. + pub fn push(&mut self, promise: Promise) { + self.0.push(promise.0) + } + + /// Get the next result from this collection, if any. + pub async fn next( + &mut self, + mut store: impl AsContextMut, + ) -> Result> { + Ok(poll_until(store.as_context_mut(), self.0.next()).await?.1) + } +} + +struct HostTaskResult { + event: u32, + param: u32, + caller: TableId, +} + +type HostTaskFuture = Pin< + Box< + dyn Future< + Output = ( + u32, + Box Result + Send + Sync>, + ), + > + Send + + Sync + + 'static, + >, +>; + +struct HostTask { + caller_instance: RuntimeComponentInstanceIndex, +} + +enum Deferred { + None, + Stackful(StoreFiber<'static>), + Stackless { + call: Box Result + Send + Sync + 'static>, + instance: RuntimeComponentInstanceIndex, + callback: SendSyncPtr, + }, +} + +impl Deferred { + fn take_fiber(&mut self) -> Option> { + if let Self::Stackful(_) = self { + let Self::Stackful(fiber) = mem::replace(self, Self::None) else { + unreachable!() + }; + Some(fiber) + } else { + None + } + } +} + +#[derive(Copy, Clone)] +struct Callback { + function: SendSyncPtr, + context: u32, + instance: RuntimeComponentInstanceIndex, +} + +enum Caller { + Host(Option>), + Guest { + task: TableId, + instance: RuntimeComponentInstanceIndex, + }, +} + +struct GuestTask { + lower_params: Option, + lift_result: Option<(RawLift, TypeTaskReturnIndex)>, + result: Option, + callback: Option, + events: VecDeque<(u32, AnyTask, u32)>, + caller: Caller, + deferred: Deferred, + should_yield: bool, +} + +impl Default for GuestTask { + fn default() -> Self { + Self { + lower_params: None, + lift_result: None, + result: None, + callback: None, + events: VecDeque::new(), + caller: Caller::Host(None), + deferred: Deferred::None, + should_yield: false, + } + } +} + +#[derive(Copy, Clone)] +enum AnyTask { + Host(TableId), + Guest(TableId), + Transmit(TableId), +} + +impl AnyTask { + fn rep(&self) -> u32 { + match self { + Self::Host(task) => task.rep(), + Self::Guest(task) => task.rep(), + Self::Transmit(task) => task.rep(), + } + } + + fn delete_all_from(&self, mut store: StoreContextMut) -> Result<()> { + match self { + Self::Host(task) => { + log::trace!("delete host task {}", task.rep()); + store.concurrent_state().table.delete(*task).map(drop) + } + Self::Guest(task) => { + let finished = store + .concurrent_state() + .table + .get(*task)? + .events + .iter() + .filter_map(|(event, call, _)| { + (*event == events::EVENT_CALL_DONE).then_some(*call) + }) + .collect::>(); + + for call in finished { + log::trace!("will delete call {}", call.rep()); + call.delete_all_from(store.as_context_mut())?; + } + + log::trace!("delete guest task {}", task.rep()); + store.concurrent_state().table.delete(*task).map(drop) + } + Self::Transmit(task) => store.concurrent_state().table.delete(*task).map(drop), + }?; + + Ok(()) + } +} + +pub(crate) struct LiftLowerContext { + pub(crate) pointer: *mut u8, + pub(crate) dropper: fn(*mut u8), +} + +unsafe impl Send for LiftLowerContext {} +unsafe impl Sync for LiftLowerContext {} + +impl Drop for LiftLowerContext { + fn drop(&mut self) { + (self.dropper)(self.pointer); + } +} + +type RawLower = + Box]) -> Result<()> + Send + Sync>; + +type LowerFn = fn(LiftLowerContext, *mut dyn VMStore, &mut [MaybeUninit]) -> Result<()>; + +type RawLift = Box< + dyn FnOnce(*mut dyn VMStore, &[ValRaw]) -> Result>> + + Send + + Sync, +>; + +type LiftFn = + fn(LiftLowerContext, *mut dyn VMStore, &[ValRaw]) -> Result>>; + +type LiftedResult = Box; + +struct Reset(*mut T, T); + +impl Drop for Reset { + fn drop(&mut self) { + unsafe { + *self.0 = self.1; + } + } +} + +struct AsyncState { + current_suspend: UnsafeCell< + *mut Suspend< + (Option<*mut dyn VMStore>, Result<()>), + Option<*mut dyn VMStore>, + (Option<*mut dyn VMStore>, Result<()>), + >, + >, + current_poll_cx: UnsafeCell<*mut Context<'static>>, +} + +unsafe impl Send for AsyncState {} +unsafe impl Sync for AsyncState {} + +pub(crate) struct AsyncCx { + current_suspend: *mut *mut wasmtime_fiber::Suspend< + (Option<*mut dyn VMStore>, Result<()>), + Option<*mut dyn VMStore>, + (Option<*mut dyn VMStore>, Result<()>), + >, + current_stack_limit: *mut usize, + current_poll_cx: *mut *mut Context<'static>, + track_pkey_context_switch: bool, +} + +impl AsyncCx { + pub(crate) fn new(store: &mut StoreContextMut) -> Self { + Self { + current_suspend: store.concurrent_state().async_state.current_suspend.get(), + current_stack_limit: store.0.runtime_limits().stack_limit.get(), + current_poll_cx: store.concurrent_state().async_state.current_poll_cx.get(), + track_pkey_context_switch: store.has_pkey(), + } + } + + unsafe fn poll(&self, mut future: Pin<&mut (dyn Future + Send)>) -> Poll { + let poll_cx = *self.current_poll_cx; + let _reset = Reset(self.current_poll_cx, poll_cx); + *self.current_poll_cx = ptr::null_mut(); + assert!(!poll_cx.is_null()); + future.as_mut().poll(&mut *poll_cx) + } + + pub(crate) unsafe fn block_on<'a, T, U>( + &self, + mut future: Pin<&mut (dyn Future + Send)>, + mut store: Option>, + ) -> Result<(U, Option>)> { + loop { + match self.poll(future.as_mut()) { + Poll::Ready(v) => break Ok((v, store)), + Poll::Pending => {} + } + + store = self.suspend(store)?; + } + } + + unsafe fn suspend<'a, T>( + &self, + store: Option>, + ) -> Result>> { + let previous_mask = if self.track_pkey_context_switch { + let previous_mask = mpk::current_mask(); + mpk::allow(ProtectionMask::all()); + previous_mask + } else { + ProtectionMask::all() + }; + let store = suspend_fiber(self.current_suspend, self.current_stack_limit, store); + if self.track_pkey_context_switch { + mpk::allow(previous_mask); + } + store + } +} + +#[derive(Default)] +struct InstanceState { + backpressure: bool, + in_sync_call: bool, + task_queue: VecDeque>, +} + +pub struct ConcurrentState { + guest_task: Option>, + futures: ReadyChunks>, + table: Table, + async_state: AsyncState, + // TODO: this can and should be a `PrimaryMap` + instance_states: HashMap, + yielding: HashSet, + unblocked: HashSet, + component_instance: Option>, + _phantom: PhantomData, +} + +impl Default for ConcurrentState { + fn default() -> Self { + Self { + guest_task: None, + table: Table::new(), + futures: ReadyChunks::new(FuturesUnordered::new(), 1024), + async_state: AsyncState { + current_suspend: UnsafeCell::new(ptr::null_mut()), + current_poll_cx: UnsafeCell::new(ptr::null_mut()), + }, + instance_states: HashMap::new(), + yielding: HashSet::new(), + unblocked: HashSet::new(), + component_instance: None, + _phantom: PhantomData, + } + } +} + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +/// Provide a hint to Rust type inferencer that we're returning a compatible +/// closure from a `LinkerInstance::func_wrap_concurrent` future. +pub fn for_any(fun: F) -> F +where + F: FnOnce(StoreContextMut) -> R + 'static, + R: 'static, +{ + fun +} + +fn for_any_lower< + F: FnOnce(*mut dyn VMStore, &mut [MaybeUninit]) -> Result<()> + Send + Sync, +>( + fun: F, +) -> F { + fun +} + +fn for_any_lift< + F: FnOnce(*mut dyn VMStore, &[ValRaw]) -> Result>> + Send + Sync, +>( + fun: F, +) -> F { + fun +} + +pub(crate) fn first_poll( + instance: *mut ComponentInstance, + mut store: StoreContextMut, + future: impl Future) -> Result + Send + Sync + 'static> + + Send + + Sync + + 'static, + caller_instance: RuntimeComponentInstanceIndex, + lower: impl FnOnce(StoreContextMut, R) -> Result<()> + Send + Sync + 'static, +) -> Result> { + let caller = store.concurrent_state().guest_task.unwrap(); + let task = store + .concurrent_state() + .table + .push_child(HostTask { caller_instance }, caller)?; + log::trace!("new child of {}: {}", caller.rep(), task.rep()); + let mut future = Box::pin(future.map(move |fun| { + ( + task.rep(), + Box::new(move |store: *mut dyn VMStore| { + let mut store = unsafe { StoreContextMut(&mut *store.cast()) }; + let result = fun(store.as_context_mut())?; + lower(store, result)?; + Ok(HostTaskResult { + event: events::EVENT_CALL_DONE, + param: 0u32, + caller, + }) + }) + as Box Result + Send + Sync>, + ) + })) as HostTaskFuture; + + Ok( + match future + .as_mut() + .poll(&mut Context::from_waker(&dummy_waker())) + { + Poll::Ready((_, fun)) => { + log::trace!("delete host task {} (already ready)", task.rep()); + store.concurrent_state().table.delete(task)?; + fun(store.0.traitobj())?; + None + } + Poll::Pending => { + store.concurrent_state().futures.get_mut().push(future); + Some( + unsafe { &mut *instance }.component_waitable_tables()[caller_instance] + .insert(task.rep(), WaitableState::Task)?, + ) + } + }, + ) +} + +pub(crate) fn poll_and_block<'a, T, R: Send + Sync + 'static>( + mut store: StoreContextMut<'a, T>, + future: impl Future) -> Result + Send + Sync + 'static> + + Send + + Sync + + 'static, + caller_instance: RuntimeComponentInstanceIndex, +) -> Result<(R, StoreContextMut<'a, T>)> { + let Some(caller) = store.concurrent_state().guest_task else { + return match pin!(future).poll(&mut Context::from_waker(&dummy_waker())) { + Poll::Ready(fun) => { + let result = fun(store.as_context_mut())?; + Ok((result, store)) + } + Poll::Pending => { + unreachable!() + } + }; + }; + let old_result = store + .concurrent_state() + .table + .get_mut(caller) + .with_context(|| format!("bad handle: {}", caller.rep()))? + .result + .take(); + let task = store + .concurrent_state() + .table + .push_child(HostTask { caller_instance }, caller)?; + log::trace!("new child of {}: {}", caller.rep(), task.rep()); + let mut future = Box::pin(future.map(move |fun| { + ( + task.rep(), + Box::new(move |store: *mut dyn VMStore| { + let mut store = unsafe { StoreContextMut(&mut *store.cast()) }; + let result = fun(store.as_context_mut())?; + store.concurrent_state().table.get_mut(caller)?.result = + Some(Box::new(result) as _); + Ok(HostTaskResult { + event: events::EVENT_CALL_DONE, + param: 0u32, + caller, + }) + }) + as Box Result + Send + Sync>, + ) + })) as HostTaskFuture; + + Ok( + match unsafe { AsyncCx::new(&mut store).poll(future.as_mut()) } { + Poll::Ready((_, fun)) => { + log::trace!("delete host task {} (already ready)", task.rep()); + store.concurrent_state().table.delete(task)?; + let store = store.0.traitobj(); + fun(store)?; + let mut store = unsafe { StoreContextMut(&mut *store.cast()) }; + let result = *mem::replace( + &mut store.concurrent_state().table.get_mut(caller)?.result, + old_result, + ) + .unwrap() + .downcast() + .unwrap(); + (result, store) + } + Poll::Pending => { + store.concurrent_state().futures.get_mut().push(future); + loop { + if let Some(result) = store + .concurrent_state() + .table + .get_mut(caller)? + .result + .take() + { + store.concurrent_state().table.get_mut(caller)?.result = old_result; + break (*result.downcast().unwrap(), store); + } else { + let async_cx = AsyncCx::new(&mut store); + store = unsafe { async_cx.suspend(Some(store)) }?.unwrap(); + } + } + } + }, + ) +} + +pub(crate) async fn on_fiber<'a, R: Send + Sync + 'static, T: Send>( + mut store: StoreContextMut<'a, T>, + instance: RuntimeComponentInstanceIndex, + func: impl FnOnce(&mut StoreContextMut) -> R + Send, +) -> Result<(R, StoreContextMut<'a, T>)> { + let result = Arc::new(Mutex::new(None)); + let mut fiber = make_fiber(&mut store, instance, { + let result = result.clone(); + move |mut store| { + *result.lock().unwrap() = Some(func(&mut store)); + Ok(()) + } + })?; + + store = poll_fn(store, move |_, mut store| { + match resume_fiber(&mut fiber, store.take(), Ok(())) { + Ok(Ok((store, result))) => Ok(result.map(|()| store)), + Ok(Err(s)) => Err(s), + Err(e) => Ok(Err(e)), + } + }) + .await?; + + let result = result.lock().unwrap().take().unwrap(); + Ok((result, store)) +} + +fn maybe_send_event<'a, T>( + mut store: StoreContextMut<'a, T>, + guest_task: TableId, + event: u32, + call: AnyTask, + result: u32, +) -> Result> { + assert_ne!(guest_task.rep(), call.rep()); + if let Some(callback) = store.concurrent_state().table.get(guest_task)?.callback { + let old_task = store.concurrent_state().guest_task.replace(guest_task); + let Some((handle, _)) = unsafe { + &mut *store + .concurrent_state() + .component_instance + .unwrap() + .as_ptr() + } + .component_waitable_tables()[callback.instance] + .get_mut_by_rep(call.rep()) + else { + bail!("handle not found for waitable rep {}", call.rep()); + }; + log::trace!( + "use callback to deliver event {event} to {} for {} (handle {handle}): {:?} {}", + guest_task.rep(), + call.rep(), + callback.function, + callback.context + ); + let params = &mut [ + ValRaw::u32(callback.context), + ValRaw::u32(event), + ValRaw::u32(handle), + ValRaw::u32(result), + ]; + unsafe { + crate::Func::call_unchecked_raw(&mut store, callback.function.as_non_null(), params)?; + } + let done = params[0].get_u32() != 0; + log::trace!("{} done? {done}", guest_task.rep()); + if done { + store.concurrent_state().table.get_mut(guest_task)?.callback = None; + + match &store.concurrent_state().table.get(guest_task)?.caller { + Caller::Guest { task, .. } => { + let task = *task; + store = maybe_send_event( + store, + task, + events::EVENT_CALL_DONE, + AnyTask::Guest(guest_task), + 0, + )?; + } + Caller::Host(_) => { + log::trace!("maybe_send_event will delete {}", call.rep()); + AnyTask::Guest(guest_task).delete_all_from(store.as_context_mut())?; + } + } + } + store.concurrent_state().guest_task = old_task; + Ok(store) + } else { + store + .concurrent_state() + .table + .get_mut(guest_task)? + .events + .push_back((event, call, result)); + + let resumed = if event == events::EVENT_CALL_DONE { + if let Some(fiber) = store + .concurrent_state() + .table + .get_mut(guest_task)? + .deferred + .take_fiber() + { + log::trace!( + "use fiber to deliver event {event} to {} for {}", + guest_task.rep(), + call.rep() + ); + let old_task = store.concurrent_state().guest_task.replace(guest_task); + store = resume_stackful(store, guest_task, fiber)?; + store.concurrent_state().guest_task = old_task; + true + } else { + false + } + } else { + false + }; + + if !resumed { + log::trace!( + "queue event {event} to {} for {}", + guest_task.rep(), + call.rep() + ); + } + + Ok(store) + } +} + +fn resume_stackful<'a, T>( + mut store: StoreContextMut<'a, T>, + guest_task: TableId, + mut fiber: StoreFiber<'static>, +) -> Result> { + match resume_fiber(&mut fiber, Some(store), Ok(()))? { + Ok((mut store, result)) => { + result?; + store = maybe_resume_next_task(store, fiber.instance)?; + for (event, call, _) in mem::take( + &mut store + .concurrent_state() + .table + .get_mut(guest_task) + .with_context(|| format!("bad handle: {}", guest_task.rep()))? + .events, + ) { + if event == events::EVENT_CALL_DONE { + log::trace!("resume_stackful will delete call {}", call.rep()); + call.delete_all_from(store.as_context_mut())?; + } + } + match &store.concurrent_state().table.get(guest_task)?.caller { + Caller::Host(_) => { + log::trace!("resume_stackful will delete task {}", guest_task.rep()); + AnyTask::Guest(guest_task).delete_all_from(store.as_context_mut())?; + Ok(store) + } + Caller::Guest { task, .. } => { + let task = *task; + maybe_send_event( + store, + task, + events::EVENT_CALL_DONE, + AnyTask::Guest(guest_task), + 0, + ) + } + } + } + Err(new_store) => { + store = new_store.unwrap(); + store.concurrent_state().table.get_mut(guest_task)?.deferred = + Deferred::Stackful(fiber); + Ok(store) + } + } +} + +fn resume_stackless<'a, T>( + store: StoreContextMut<'a, T>, + guest_task: TableId, + call: Box Result>, + instance: RuntimeComponentInstanceIndex, + callback: SendSyncPtr, +) -> Result> { + let store = store.0.traitobj(); + let guest_context = call(store)?; + let mut store = unsafe { StoreContextMut(&mut *store.cast()) }; + + let task = store.concurrent_state().table.get_mut(guest_task)?; + let event = if task.lift_result.is_some() { + events::EVENT_CALL_STARTED + } else if guest_context != 0 { + events::EVENT_CALL_RETURNED + } else { + events::EVENT_CALL_DONE + }; + if guest_context != 0 { + log::trace!("set callback for {}", guest_task.rep()); + task.callback = Some(Callback { + function: callback, + instance, + context: guest_context, + }); + for (event, call, result) in mem::take(&mut task.events) { + store = maybe_send_event(store, guest_task, event, call, result)?; + } + } + store = maybe_resume_next_task(store, instance)?; + if let Caller::Guest { task, .. } = &store.concurrent_state().table.get(guest_task)?.caller { + let task = *task; + maybe_send_event(store, task, event, AnyTask::Guest(guest_task), 0) + } else { + Ok(store) + } +} + +fn poll_for_result<'a, T>(mut store: StoreContextMut<'a, T>) -> Result> { + let task = store.concurrent_state().guest_task; + poll_loop(store, move |store| { + task.map(|task| { + Ok::<_, anyhow::Error>(store.concurrent_state().table.get(task)?.result.is_none()) + }) + .unwrap_or(Ok(true)) + }) +} + +fn handle_ready<'a, T>( + mut store: StoreContextMut<'a, T>, + ready: Vec<( + u32, + Box Result + Send + Sync>, + )>, +) -> Result> { + for (task, fun) in ready { + let vm_store = store.0.traitobj(); + let result = fun(vm_store)?; + store = unsafe { StoreContextMut::(&mut *vm_store.cast()) }; + let task = match result.event { + events::EVENT_CALL_DONE => AnyTask::Host(TableId::::new(task)), + events::EVENT_STREAM_READ + | events::EVENT_FUTURE_READ + | events::EVENT_STREAM_WRITE + | events::EVENT_FUTURE_WRITE => AnyTask::Transmit(TableId::::new(task)), + _ => unreachable!(), + }; + store = maybe_send_event(store, result.caller, result.event, task, result.param)?; + } + Ok(store) +} + +fn maybe_yield<'a, T>(mut store: StoreContextMut<'a, T>) -> Result> { + let guest_task = store.concurrent_state().guest_task.unwrap(); + + if store.concurrent_state().table.get(guest_task)?.should_yield { + log::trace!("maybe_yield suspend {}", guest_task.rep()); + + store.concurrent_state().yielding.insert(guest_task.rep()); + let cx = AsyncCx::new(&mut store); + store = unsafe { cx.suspend(Some(store)) }?.unwrap(); + + log::trace!("maybe_yield resume {}", guest_task.rep()); + } else { + log::trace!("maybe_yield skip {}", guest_task.rep()); + } + + Ok(store) +} + +fn unyield<'a, T>(mut store: StoreContextMut<'a, T>) -> Result<(StoreContextMut<'a, T>, bool)> { + let mut resumed = false; + for task in mem::take(&mut store.concurrent_state().yielding) { + let guest_task = TableId::::new(task); + if let Some(fiber) = store + .concurrent_state() + .table + .get_mut(guest_task)? + .deferred + .take_fiber() + { + resumed = true; + let old_task = store.concurrent_state().guest_task.replace(guest_task); + store = resume_stackful(store, guest_task, fiber)?; + store.concurrent_state().guest_task = old_task; + } + } + + for instance in mem::take(&mut store.concurrent_state().unblocked) { + let entry = store + .concurrent_state() + .instance_states + .entry(instance) + .or_default(); + + if !(entry.backpressure || entry.in_sync_call) { + if let Some(task) = entry.task_queue.pop_front() { + resumed = true; + store = resume(store, task)?; + } + } + } + + Ok((store, resumed)) +} + +fn poll_loop<'a, T>( + mut store: StoreContextMut<'a, T>, + mut continue_: impl FnMut(&mut StoreContextMut<'a, T>) -> Result, +) -> Result> { + loop { + let cx = AsyncCx::new(&mut store); + let mut future = pin!(store.concurrent_state().futures.next()); + let ready = unsafe { cx.poll(future.as_mut()) }; + + match ready { + Poll::Ready(Some(ready)) => { + store = handle_ready(store, ready)?; + } + Poll::Ready(None) => { + let (s, resumed) = unyield(store)?; + store = s; + if !resumed { + log::trace!("exhausted future queue; exiting poll_loop"); + break; + } + } + Poll::Pending => { + let (s, resumed) = unyield(store)?; + store = s; + if continue_(&mut store)? { + let cx = AsyncCx::new(&mut store); + store = unsafe { cx.suspend(Some(store)) }?.unwrap(); + } else if !resumed { + break; + } + } + } + } + + Ok(store) +} + +fn resume<'a, T>( + mut store: StoreContextMut<'a, T>, + task: TableId, +) -> Result> { + log::trace!("resume {}", task.rep()); + + // TODO: Avoid calling `resume_stackful` or `resume_stackless` here, because it may call us, leading to + // recursion limited only by the number of waiters. Flatten this into an iteration instead. + let old_task = store.concurrent_state().guest_task.replace(task); + store = match mem::replace( + &mut store.concurrent_state().table.get_mut(task)?.deferred, + Deferred::None, + ) { + Deferred::None => unreachable!(), + Deferred::Stackful(fiber) => resume_stackful(store, task, fiber), + Deferred::Stackless { + call, + instance, + callback, + } => resume_stackless(store, task, call, instance, callback), + }?; + store.concurrent_state().guest_task = old_task; + Ok(store) +} + +fn maybe_resume_next_task<'a, T>( + mut store: StoreContextMut<'a, T>, + instance: RuntimeComponentInstanceIndex, +) -> Result> { + let state = store + .concurrent_state() + .instance_states + .get_mut(&instance) + .unwrap(); + + if state.backpressure || state.in_sync_call { + Ok(store) + } else { + if let Some(next) = state.task_queue.pop_front() { + resume(store, next) + } else { + Ok(store) + } + } +} + +struct StoreFiber<'a> { + fiber: Option< + Fiber< + 'a, + (Option<*mut dyn VMStore>, Result<()>), + Option<*mut dyn VMStore>, + (Option<*mut dyn VMStore>, Result<()>), + >, + >, + state: Option, + engine: Engine, + suspend: *mut *mut Suspend< + (Option<*mut dyn VMStore>, Result<()>), + Option<*mut dyn VMStore>, + (Option<*mut dyn VMStore>, Result<()>), + >, + stack_limit: *mut usize, + instance: RuntimeComponentInstanceIndex, +} + +impl<'a> Drop for StoreFiber<'a> { + fn drop(&mut self) { + if !self.fiber.as_ref().unwrap().done() { + let result = unsafe { resume_fiber_raw(self, None, Err(anyhow!("future dropped"))) }; + debug_assert!(result.is_ok()); + } + + self.state.take().unwrap().assert_null(); + + unsafe { + self.engine + .allocator() + .deallocate_fiber_stack(self.fiber.take().unwrap().into_stack()); + } + } +} + +unsafe impl<'a> Send for StoreFiber<'a> {} +unsafe impl<'a> Sync for StoreFiber<'a> {} + +fn make_fiber<'a, T>( + store: &mut StoreContextMut, + instance: RuntimeComponentInstanceIndex, + fun: impl FnOnce(StoreContextMut) -> Result<()> + 'a, +) -> Result> { + let engine = store.engine().clone(); + let stack = engine.allocator().allocate_fiber_stack()?; + Ok(StoreFiber { + fiber: Some(Fiber::new( + stack, + move |(store_ptr, result): (Option<*mut dyn VMStore>, Result<()>), suspend| { + if result.is_err() { + (store_ptr, result) + } else { + unsafe { + let store_ptr = store_ptr.unwrap(); + let mut store = StoreContextMut(&mut *store_ptr.cast()); + let suspend_ptr = + store.concurrent_state().async_state.current_suspend.get(); + let _reset = Reset(suspend_ptr, *suspend_ptr); + *suspend_ptr = suspend; + (Some(store_ptr), fun(store.as_context_mut())) + } + } + }, + )?), + state: Some(AsyncWasmCallState::new()), + engine, + suspend: store.concurrent_state().async_state.current_suspend.get(), + stack_limit: store.0.runtime_limits().stack_limit.get(), + instance, + }) +} + +unsafe fn resume_fiber_raw<'a>( + fiber: *mut StoreFiber<'a>, + store: Option<*mut dyn VMStore>, + result: Result<()>, +) -> Result<(Option<*mut dyn VMStore>, Result<()>), Option<*mut dyn VMStore>> { + struct Restore<'a> { + fiber: *mut StoreFiber<'a>, + state: Option, + } + + impl Drop for Restore<'_> { + fn drop(&mut self) { + unsafe { + (*self.fiber).state = Some(self.state.take().unwrap().restore()); + } + } + } + + let _reset_suspend = Reset((*fiber).suspend, *(*fiber).suspend); + let _reset_stack_limit = Reset((*fiber).stack_limit, *(*fiber).stack_limit); + let state = Some((*fiber).state.take().unwrap().push()); + let restore = Restore { fiber, state }; + (*restore.fiber) + .fiber + .as_ref() + .unwrap() + .resume((store, result)) +} + +fn poll_ready<'a, T>(mut store: StoreContextMut<'a, T>) -> Result> { + unsafe { + let cx = *store.concurrent_state().async_state.current_poll_cx.get(); + assert!(!cx.is_null()); + while let Poll::Ready(Some(ready)) = + store.concurrent_state().futures.poll_next_unpin(&mut *cx) + { + match handle_ready(store, ready) { + Ok(s) => { + store = s; + } + Err(e) => { + return Err(e); + } + } + } + } + Ok(store) +} + +fn resume_fiber<'a, T>( + fiber: &mut StoreFiber, + mut store: Option>, + result: Result<()>, +) -> Result, Result<()>), Option>>> { + if let Some(s) = store.take() { + store = Some(poll_ready(s)?); + } + + unsafe { + match resume_fiber_raw(fiber, store.map(|s| s.0.traitobj()), result) + .map(|(store, result)| (StoreContextMut(&mut *store.unwrap().cast()), result)) + .map_err(|v| v.map(|v| StoreContextMut(&mut *v.cast()))) + { + Ok(pair) => Ok(Ok(pair)), + Err(s) => { + if let Some(range) = fiber.fiber.as_ref().unwrap().stack().range() { + AsyncWasmCallState::assert_current_state_not_in_range(range); + } + + Ok(Err(s)) + } + } + } +} + +unsafe fn suspend_fiber<'a, T>( + suspend: *mut *mut Suspend< + (Option<*mut dyn VMStore>, Result<()>), + Option<*mut dyn VMStore>, + (Option<*mut dyn VMStore>, Result<()>), + >, + stack_limit: *mut usize, + store: Option>, +) -> Result>> { + let _reset_suspend = Reset(suspend, *suspend); + let _reset_stack_limit = Reset(stack_limit, *stack_limit); + let (store, result) = (**suspend).suspend(store.map(|s| s.0.traitobj())); + result?; + Ok(store.map(|v| StoreContextMut(&mut *v.cast()))) +} + +enum TaskCheck { + Wait(*mut VMMemoryDefinition, u32, RuntimeComponentInstanceIndex), + Poll(*mut VMMemoryDefinition, u32, RuntimeComponentInstanceIndex), + Yield, +} + +unsafe fn task_check(cx: *mut VMOpaqueContext, async_: bool, check: TaskCheck) -> Result { + if async_ { + bail!("todo: async `task.wait`, `task.poll`, and `task.yield` not yet implemented"); + } + + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + + let guest_task = cx.concurrent_state().guest_task.unwrap(); + + log::trace!("task check for {}", guest_task.rep()); + + let wait = matches!(check, TaskCheck::Wait(..)); + + if wait + && cx + .concurrent_state() + .table + .get(guest_task)? + .callback + .is_some() + { + bail!("cannot call `task.wait` from async-lifted export with callback"); + } + + if matches!(check, TaskCheck::Yield) + || cx + .concurrent_state() + .table + .get(guest_task)? + .events + .is_empty() + { + cx = maybe_yield(cx)?; + + if cx + .concurrent_state() + .table + .get(guest_task)? + .events + .is_empty() + { + cx = poll_loop(cx, move |cx| { + Ok::<_, anyhow::Error>( + wait && cx + .concurrent_state() + .table + .get(guest_task)? + .events + .is_empty(), + ) + })?; + } + } + + log::trace!("task check for {}, part two", guest_task.rep()); + + let result = match check { + TaskCheck::Wait(memory, payload, caller_instance) => { + let (event, call, result) = cx + .concurrent_state() + .table + .get_mut(guest_task)? + .events + .pop_front() + .ok_or_else(|| anyhow!("no tasks to wait for"))?; + + log::trace!( + "deliver event {event} via task.wait to {} for {}", + guest_task.rep(), + call.rep() + ); + + let Some((handle, _)) = + (*instance).component_waitable_tables()[caller_instance].get_mut_by_rep(call.rep()) + else { + bail!("handle not found for waitable rep {}", call.rep()); + }; + + let options = Options::new( + cx.0.id(), + NonNull::new(memory), + None, + StringEncoding::Utf8, + true, + None, + ); + let types = (*instance).component_types(); + let ptr = + func::validate_inbounds::(options.memory_mut(cx.0), &ValRaw::u32(payload))?; + let mut lower = LowerContext::new(cx, &options, types, instance); + handle.store(&mut lower, InterfaceType::U32, ptr)?; + result.store(&mut lower, InterfaceType::U32, ptr + 4)?; + + Ok(event) + } + TaskCheck::Poll(memory, payload, caller_instance) => { + if let Some((event, call, result)) = cx + .concurrent_state() + .table + .get_mut(guest_task)? + .events + .pop_front() + { + log::trace!( + "deliver event {event} via task.poll to {} for {}", + guest_task.rep(), + call.rep() + ); + + let Some((handle, _)) = (*instance).component_waitable_tables()[caller_instance] + .get_mut_by_rep(call.rep()) + else { + bail!("handle not found for waitable rep {}", call.rep()); + }; + + let options = Options::new( + cx.0.id(), + NonNull::new(memory), + None, + StringEncoding::Utf8, + true, + None, + ); + let types = (*instance).component_types(); + let ptr = func::validate_inbounds::<(u32, u32)>( + options.memory_mut(cx.0), + &ValRaw::u32(payload), + )?; + let mut lower = LowerContext::new(cx, &options, types, instance); + event.store(&mut lower, InterfaceType::U32, ptr)?; + handle.store(&mut lower, InterfaceType::U32, ptr + 4)?; + result.store(&mut lower, InterfaceType::U32, ptr + 8)?; + + Ok(1) + } else { + log::trace!( + "no events ready to deliver via task.poll to {}", + guest_task.rep() + ); + + Ok(0) + } + } + TaskCheck::Yield => Ok(0), + }; + + result +} + +unsafe fn call_host_and_handle_result( + cx: *mut VMOpaqueContext, + func: impl FnOnce() -> Result, +) -> R::Abi { + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let raw_store = (*instance).store(); + let store = StoreContextMut::(&mut *raw_store.cast()); + + crate::runtime::vm::catch_unwind_and_record_trap(|| { + store.0.call_hook(CallHook::CallingHost)?; + let res = func(); + store.0.call_hook(CallHook::ReturningFromHost)?; + res + }) +} + +pub(crate) extern "C" fn task_backpressure( + cx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + enabled: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(cx, || { + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let entry = cx + .concurrent_state() + .instance_states + .entry(caller_instance) + .or_default(); + let old = entry.backpressure; + let new = enabled != 0; + entry.backpressure = new; + + if old && !new && !entry.task_queue.is_empty() { + cx.concurrent_state().unblocked.insert(caller_instance); + } + + Ok(()) + }) + } +} + +pub(crate) extern "C" fn task_return( + cx: *mut VMOpaqueContext, + ty: TypeTaskReturnIndex, + storage: *mut MaybeUninit, + storage_len: usize, +) -> bool { + unsafe { + call_host_and_handle_result::(cx, || { + let storage = std::slice::from_raw_parts(storage, storage_len); + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let guest_task = cx.concurrent_state().guest_task.unwrap(); + let (lift, lift_ty) = cx + .concurrent_state() + .table + .get_mut(guest_task)? + .lift_result + .take() + .ok_or_else(|| anyhow!("`task.return` called more than once"))?; + + if ty != lift_ty { + bail!("invalid `task.return` signature for current task"); + } + + assert!(cx + .concurrent_state() + .table + .get(guest_task)? + .result + .is_none()); + + let cx = cx.0.traitobj(); + let result = lift( + cx, + mem::transmute::<&[MaybeUninit], &[ValRaw]>(storage), + )?; + + let mut cx = StoreContextMut::(&mut *cx.cast()); + if let Caller::Host(tx) = &mut cx.concurrent_state().table.get_mut(guest_task)?.caller { + _ = tx.take().unwrap().send(result.unwrap()); + } else { + cx.concurrent_state().table.get_mut(guest_task)?.result = result; + } + + Ok(()) + }) + } +} + +pub(crate) extern "C" fn task_wait( + cx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: *mut VMMemoryDefinition, + payload: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(cx, || { + task_check::( + cx, + async_, + TaskCheck::Wait(memory, payload, caller_instance), + ) + }) + } +} + +pub(crate) extern "C" fn task_poll( + cx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: *mut VMMemoryDefinition, + payload: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(cx, || { + task_check::( + cx, + async_, + TaskCheck::Poll(memory, payload, caller_instance), + ) + }) + } +} + +pub(crate) extern "C" fn task_yield(cx: *mut VMOpaqueContext, async_: bool) -> bool { + unsafe { + call_host_and_handle_result::(cx, || { + task_check::(cx, async_, TaskCheck::Yield)?; + Ok(()) + }) + } +} + +pub(crate) extern "C" fn subtask_drop( + cx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + task_id: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(cx, || { + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let (rep, WaitableState::Task) = (*instance).component_waitable_tables() + [caller_instance] + .remove_by_index(task_id)? + else { + bail!("invalid task handle: {task_id}"); + }; + let table = &mut cx.concurrent_state().table; + log::trace!("subtask_drop delete {rep}"); + let task = table.delete_any(rep)?; + let expected_caller_instance = match task.downcast::() { + Ok(task) => task.caller_instance, + Err(task) => match task.downcast::() { + Ok(task) => { + if let Caller::Guest { instance, .. } = task.caller { + instance + } else { + unreachable!() + } + } + Err(_) => unreachable!(), + }, + }; + assert_eq!(expected_caller_instance, caller_instance); + Ok(()) + }) + } +} + +pub(crate) extern "C" fn async_enter( + cx: *mut VMOpaqueContext, + start: *mut VMFuncRef, + return_: *mut VMFuncRef, + caller_instance: RuntimeComponentInstanceIndex, + task_return_type: TypeTaskReturnIndex, + params: u32, + results: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(cx, || { + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let start = SendSyncPtr::new(NonNull::new(start).unwrap()); + let return_ = SendSyncPtr::new(NonNull::new(return_).unwrap()); + let old_task = cx.concurrent_state().guest_task.take(); + let old_task_rep = old_task.map(|v| v.rep()); + let new_task = GuestTask { + lower_params: Some(Box::new(move |cx, dst| { + let mut cx = StoreContextMut::(&mut *cx.cast()); + assert!(dst.len() <= MAX_FLAT_PARAMS); + let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; + src[0] = MaybeUninit::new(ValRaw::u32(params)); + crate::Func::call_unchecked_raw( + &mut cx, + start.as_non_null(), + &mut src[..1.max(dst.len())] as *mut [MaybeUninit] as _, + )?; + dst.copy_from_slice(&src[..dst.len()]); + let task = cx.concurrent_state().guest_task.unwrap(); + if let Some(rep) = old_task_rep { + maybe_send_event( + cx, + TableId::new(rep), + events::EVENT_CALL_STARTED, + AnyTask::Guest(task), + 0, + )?; + } + Ok(()) + })), + lift_result: Some(( + Box::new(move |cx, src| { + let mut cx = StoreContextMut::(&mut *cx.cast()); + let mut my_src = src.to_owned(); // TODO: use stack to avoid allocation? + my_src.push(ValRaw::u32(results)); + crate::Func::call_unchecked_raw( + &mut cx, + return_.as_non_null(), + my_src.as_mut_slice(), + )?; + let task = cx.concurrent_state().guest_task.unwrap(); + if let Some(rep) = old_task_rep { + maybe_send_event( + cx, + TableId::new(rep), + events::EVENT_CALL_RETURNED, + AnyTask::Guest(task), + 0, + )?; + } + Ok(None) + }), + task_return_type, + )), + result: None, + callback: None, + caller: Caller::Guest { + task: old_task.unwrap(), + instance: caller_instance, + }, + deferred: Deferred::None, + events: VecDeque::new(), + should_yield: false, + }; + let guest_task = if let Some(old_task) = old_task { + let child = cx.concurrent_state().table.push_child(new_task, old_task)?; + log::trace!("new child of {}: {}", old_task.rep(), child.rep()); + child + } else { + cx.concurrent_state().table.push(new_task)? + }; + + cx.concurrent_state().guest_task = Some(guest_task); + + Ok(()) + }) + } +} + +fn make_call( + guest_task: TableId, + callee: SendSyncPtr, + param_count: usize, + result_count: usize, +) -> impl FnOnce( + StoreContextMut, +) -> Result<([MaybeUninit; MAX_FLAT_PARAMS], StoreContextMut)> + + Send + + Sync + + 'static { + move |mut cx: StoreContextMut| { + let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; + let lower = cx + .concurrent_state() + .table + .get_mut(guest_task)? + .lower_params + .take() + .unwrap(); + let cx = cx.0.traitobj(); + lower(cx, &mut storage[..param_count])?; + let mut cx = unsafe { StoreContextMut::(&mut *cx.cast()) }; + + unsafe { + crate::Func::call_unchecked_raw( + &mut cx, + callee.as_non_null(), + &mut storage[..param_count.max(result_count)] as *mut [MaybeUninit] as _, + )?; + } + + Ok((storage, cx)) + } +} + +fn do_start_call<'a, T>( + mut cx: StoreContextMut<'a, T>, + guest_task: TableId, + async_: bool, + call: impl FnOnce( + StoreContextMut, + ) -> Result<([MaybeUninit; MAX_FLAT_PARAMS], StoreContextMut)> + + Send + + Sync + + 'static, + callback: Option>, + callee_instance: RuntimeComponentInstanceIndex, + result_count: usize, +) -> Result<(u32, StoreContextMut<'a, T>)> { + let state = &mut cx + .concurrent_state() + .instance_states + .entry(callee_instance) + .or_default(); + let ready = state.task_queue.is_empty() && !(state.backpressure || state.in_sync_call); + + let mut guest_context = 0; + + let mut cx = if let Some(callback) = callback { + assert!(async_); + + if ready { + let (storage, cx) = call(cx)?; + guest_context = unsafe { storage[0].assume_init() }.get_i32() as u32; + cx + } else { + cx.concurrent_state() + .instance_states + .get_mut(&callee_instance) + .unwrap() + .task_queue + .push_back(guest_task); + + cx.concurrent_state().table.get_mut(guest_task)?.deferred = Deferred::Stackless { + call: Box::new(move |cx| { + let mut cx = unsafe { StoreContextMut(&mut *cx.cast()) }; + let old_task = cx.concurrent_state().guest_task.replace(guest_task); + let (storage, mut cx) = call(cx)?; + cx.concurrent_state().guest_task = old_task; + Ok(unsafe { storage[0].assume_init() }.get_i32() as u32) + }), + instance: callee_instance, + callback, + }; + cx + } + } else { + let mut fiber = make_fiber(&mut cx, callee_instance, move |mut cx| { + if !async_ { + cx.concurrent_state() + .instance_states + .get_mut(&callee_instance) + .unwrap() + .in_sync_call = true; + } + + let (storage, mut cx) = call(cx)?; + + if !async_ { + cx.concurrent_state() + .instance_states + .get_mut(&callee_instance) + .unwrap() + .in_sync_call = false; + + let (lift, _) = cx + .concurrent_state() + .table + .get_mut(guest_task)? + .lift_result + .take() + .unwrap(); + + assert!(cx + .concurrent_state() + .table + .get(guest_task)? + .result + .is_none()); + + let cx = cx.0.traitobj(); + let result = lift(cx, unsafe { + mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..result_count]) + })?; + let mut cx = unsafe { StoreContextMut::(&mut *cx.cast()) }; + + // TODO: call post_return if necessary + + if let Caller::Host(tx) = + &mut cx.concurrent_state().table.get_mut(guest_task)?.caller + { + _ = tx.take().unwrap().send(result.unwrap()); + } else { + cx.concurrent_state().table.get_mut(guest_task)?.result = result; + } + } + + Ok(()) + })?; + + cx.concurrent_state() + .table + .get_mut(guest_task)? + .should_yield = true; + + if ready { + let mut cx = Some(cx); + loop { + match resume_fiber(&mut fiber, cx.take(), Ok(()))? { + Ok((cx, result)) => { + result?; + break maybe_resume_next_task(cx, callee_instance)?; + } + Err(cx) => { + if let Some(mut cx) = cx { + cx.concurrent_state().table.get_mut(guest_task)?.deferred = + Deferred::Stackful(fiber); + break cx; + } else { + unsafe { suspend_fiber::(fiber.suspend, fiber.stack_limit, None)? }; + } + } + } + } + } else { + cx.concurrent_state() + .instance_states + .get_mut(&callee_instance) + .unwrap() + .task_queue + .push_back(guest_task); + + cx.concurrent_state().table.get_mut(guest_task)?.deferred = Deferred::Stackful(fiber); + cx + } + }; + + let guest_task = cx.concurrent_state().guest_task.take().unwrap(); + + let caller = + if let Caller::Guest { task, .. } = &cx.concurrent_state().table.get(guest_task)?.caller { + Some(*task) + } else { + None + }; + cx.concurrent_state().guest_task = caller; + + let task = cx.concurrent_state().table.get_mut(guest_task)?; + + if guest_context != 0 { + log::trace!("set callback for {}", guest_task.rep()); + task.callback = Some(Callback { + function: callback.unwrap(), + instance: callee_instance, + context: guest_context, + }); + for (event, call, result) in mem::take(&mut task.events) { + cx = maybe_send_event(cx, guest_task, event, call, result)?; + } + } + + Ok((guest_context, cx)) +} + +pub(crate) extern "C" fn async_exit( + cx: *mut VMOpaqueContext, + callback: *mut VMFuncRef, + caller_instance: RuntimeComponentInstanceIndex, + callee: *mut VMFuncRef, + callee_instance: RuntimeComponentInstanceIndex, + param_count: u32, + result_count: u32, + flags: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(cx, || { + let cx = VMComponentContext::from_opaque(cx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + + let guest_task = cx.concurrent_state().guest_task.unwrap(); + let callee = SendSyncPtr::new(NonNull::new(callee).unwrap()); + let param_count = usize::try_from(param_count).unwrap(); + assert!(param_count <= MAX_FLAT_PARAMS); + let result_count = usize::try_from(result_count).unwrap(); + assert!(result_count <= MAX_FLAT_RESULTS); + + let call = make_call(guest_task, callee, param_count, result_count); + + let (guest_context, new_cx) = do_start_call( + cx, + guest_task, + (flags & EXIT_FLAG_ASYNC_CALLEE) != 0, + call, + NonNull::new(callback).map(SendSyncPtr::new), + callee_instance, + result_count, + )?; + + cx = new_cx; + + let task = cx.concurrent_state().table.get(guest_task)?; + + let mut status = if task.lower_params.is_some() { + STATUS_STARTING + } else if task.lift_result.is_some() { + STATUS_STARTED + } else if guest_context != 0 || callback.is_null() { + STATUS_RETURNED + } else { + STATUS_DONE + }; + + let call = if status != STATUS_DONE { + if (flags & EXIT_FLAG_ASYNC_CALLER) != 0 { + (*instance).component_waitable_tables()[caller_instance] + .insert(guest_task.rep(), WaitableState::Task)? + } else { + poll_for_result(cx)?; + status = STATUS_DONE; + 0 + } + } else { + 0 + }; + + Ok((status << 30) | call) + }) + } +} + +pub(crate) fn start_call<'a, T: Send, LowerParams: Copy, R: 'static>( + mut store: StoreContextMut<'a, T>, + lower_params: LowerFn, + lower_context: LiftLowerContext, + lift_result: LiftFn, + lift_context: LiftLowerContext, + handle: Func, +) -> Result<(Promise, StoreContextMut<'a, T>)> { + // TODO: Check to see if the callee is using the memory64 ABI, in which case we must use task_return_type64. + // How do we check that? + let func_data = &store.0[handle.0]; + let task_return_type = func_data.types[func_data.ty].task_return_type32; + let is_concurrent = func_data.options.async_(); + let instance = func_data.component_instance; + let callee = func_data.export.func_ref; + let callback = func_data.options.callback; + + assert!(store.concurrent_state().guest_task.is_none()); + + // TODO: Can we safely leave this set? Can the same store be used with more than one ComponentInstance? Could + // we instead set this when the ConcurrentState is created so we don't have to set/unset it on the fly? + store.concurrent_state().component_instance = Some( + store.0[store.0[handle.0].instance.0] + .as_ref() + .unwrap() + .state + .ptr, + ); + + let (tx, rx) = oneshot::channel(); + + let guest_task = store.concurrent_state().table.push(GuestTask { + lower_params: Some(Box::new(for_any_lower(move |store, params| { + lower_params(lower_context, store, params) + })) as RawLower), + lift_result: Some(( + Box::new(for_any_lift(move |store, result| { + lift_result(lift_context, store, result) + })) as RawLift, + task_return_type, + )), + caller: Caller::Host(Some(tx)), + ..GuestTask::default() + })?; + + log::trace!("starting call {}", guest_task.rep()); + + let call = make_call( + guest_task, + SendSyncPtr::new(callee), + mem::size_of::() / mem::size_of::(), + 1, + ); + + store.concurrent_state().guest_task = Some(guest_task); + + store = do_start_call( + store, + guest_task, + is_concurrent, + call, + callback.map(SendSyncPtr::new), + instance, + 1, + )? + .1; + + store.concurrent_state().guest_task = None; + + log::trace!("started call {}", guest_task.rep()); + + Ok(( + Promise(Box::pin( + rx.map(|result| *result.unwrap().downcast().unwrap()), + )), + store, + )) +} + +pub(crate) fn call<'a, T: Send, LowerParams: Copy, R: 'static>( + store: StoreContextMut<'a, T>, + lower_params: LowerFn, + lower_context: LiftLowerContext, + lift_result: LiftFn, + lift_context: LiftLowerContext, + handle: Func, +) -> Result<(R, StoreContextMut<'a, T>)> { + let (promise, mut store) = start_call::<_, LowerParams, R>( + store, + lower_params, + lower_context, + lift_result, + lift_context, + handle, + )?; + + let mut future = promise.into_future(); + let result = Arc::new(Mutex::new(None)); + store = poll_loop(store, { + let result = result.clone(); + move |store| { + let cx = AsyncCx::new(store); + let ready = unsafe { cx.poll(future.as_mut()) }; + Ok(match ready { + Poll::Ready(value) => { + *result.lock().unwrap() = Some(value); + false + } + Poll::Pending => true, + }) + } + })?; + + let result = result.lock().unwrap().take(); + if let Some(result) = result { + Ok((result, store)) + } else { + // All outstanding host tasks completed, but the guest never yielded a result. + Err(anyhow!(crate::Trap::NoAsyncResult)) + } +} + +pub(crate) async fn poll_until<'a, T: Send, U>( + mut store: StoreContextMut<'a, T>, + future: impl Future, +) -> Result<(StoreContextMut<'a, T>, U)> { + let mut future = Box::pin(future); + loop { + loop { + let mut ready = pin!(store.concurrent_state().futures.next()); + + let mut ready = future::poll_fn({ + move |cx| { + Poll::Ready(match ready.as_mut().poll(cx) { + Poll::Ready(Some(value)) => Some(value), + Poll::Ready(None) | Poll::Pending => None, + }) + } + }) + .await; + + if ready.is_some() { + store = poll_fn(store, move |_, mut store| { + Ok(handle_ready(store.take().unwrap(), ready.take().unwrap())) + }) + .await?; + } else { + let (s, resumed) = poll_fn(store, move |_, mut store| { + Ok(unyield(store.take().unwrap())) + }) + .await?; + store = s; + if !resumed { + break; + } + } + } + + let ready = pin!(store.concurrent_state().futures.next()); + + match future::select(ready, future).await { + Either::Left((None, future_again)) => break Ok((store, future_again.await)), + Either::Left((Some(ready), future_again)) => { + let mut ready = Some(ready); + store = poll_fn(store, move |_, mut store| { + Ok(handle_ready(store.take().unwrap(), ready.take().unwrap())) + }) + .await?; + future = future_again; + } + Either::Right((result, _)) => break Ok((store, result)), + } + } +} + +async fn poll_fn<'a, T, R>( + mut store: StoreContextMut<'a, T>, + mut fun: impl FnMut( + &mut Context, + Option>, + ) -> Result>>, +) -> R { + #[derive(Clone, Copy)] + struct PollCx(*mut *mut Context<'static>); + + unsafe impl Send for PollCx {} + + let poll_cx = PollCx(store.concurrent_state().async_state.current_poll_cx.get()); + future::poll_fn({ + let mut store = Some(store); + + move |cx| unsafe { + let _reset = Reset(poll_cx.0, *poll_cx.0); + *poll_cx.0 = mem::transmute::<&mut Context<'_>, *mut Context<'static>>(cx); + #[allow(dropping_copy_types)] + drop(poll_cx); + + match fun(cx, store.take()) { + Ok(v) => Poll::Ready(v), + Err(s) => { + store = s; + Poll::Pending + } + } + } + }) + .await +} diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs new file mode 100644 index 000000000000..e57e257b9027 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -0,0 +1,2007 @@ +use { + super::{ + call_host_and_handle_result, events, table::TableId, GuestTask, HostTaskFuture, + HostTaskResult, Promise, + }, + crate::{ + component::{ + func::{self, LiftContext, LowerContext, Options}, + matching::InstanceType, + values::{ErrorContextAny, FutureAny, StreamAny}, + Val, WasmList, + }, + vm::{ + component::{ + ComponentInstance, StateTable, StreamFutureState, VMComponentContext, WaitableState, + }, + SendSyncPtr, VMFuncRef, VMMemoryDefinition, VMOpaqueContext, VMStore, + }, + AsContextMut, StoreContextMut, + }, + anyhow::{anyhow, bail, Context, Result}, + futures::{ + channel::oneshot, + future::{self, FutureExt}, + }, + std::{ + any::Any, + boxed::Box, + marker::PhantomData, + mem::{self, MaybeUninit}, + ptr::NonNull, + string::ToString, + sync::Arc, + vec::Vec, + }, + wasmtime_environ::component::{ + CanonicalAbiInfo, ComponentTypes, InterfaceType, StringEncoding, + TypeErrorContextTableIndex, TypeFutureTableIndex, TypeStreamTableIndex, + }, +}; + +// TODO: add `validate_inbounds` calls where appropriate + +const BLOCKED: usize = 0xffff_ffff; +const CLOSED: usize = 0x8000_0000; + +#[derive(Copy, Clone, Debug)] +enum TableIndex { + Stream(TypeStreamTableIndex), + Future(TypeFutureTableIndex), +} + +fn payload(ty: TableIndex, types: &Arc) -> Option { + match ty { + TableIndex::Future(ty) => types[types[ty].ty].payload, + TableIndex::Stream(ty) => Some(types[types[ty].ty].payload), + } +} + +fn state_table(instance: &mut ComponentInstance, ty: TableIndex) -> &mut StateTable { + let runtime_instance = match ty { + TableIndex::Stream(ty) => instance.component_types()[ty].instance, + TableIndex::Future(ty) => instance.component_types()[ty].instance, + }; + &mut instance.component_waitable_tables()[runtime_instance] +} + +fn push_event( + mut store: StoreContextMut, + rep: u32, + event: u32, + param: usize, + caller: TableId, +) { + store + .concurrent_state() + .futures + .get_mut() + .push(Box::pin(future::ready(( + rep, + Box::new(move |_| { + Ok(HostTaskResult { + event, + param: u32::try_from(param).unwrap(), + caller, + }) + }) + as Box Result + Send + Sync>, + ))) as HostTaskFuture); +} + +fn get_mut_by_index( + instance: &mut ComponentInstance, + ty: TableIndex, + index: u32, +) -> Result<(u32, &mut StreamFutureState)> { + get_mut_by_index_from(state_table(instance, ty), ty, index) +} + +fn get_mut_by_index_from( + state_table: &mut StateTable, + ty: TableIndex, + index: u32, +) -> Result<(u32, &mut StreamFutureState)> { + Ok(match ty { + TableIndex::Stream(ty) => { + let (rep, WaitableState::Stream(actual_ty, state)) = + state_table.get_mut_by_index(index)? + else { + bail!("invalid stream handle"); + }; + if *actual_ty != ty { + bail!("invalid stream handle"); + } + (rep, state) + } + TableIndex::Future(ty) => { + let (rep, WaitableState::Future(actual_ty, state)) = + state_table.get_mut_by_index(index)? + else { + bail!("invalid future handle"); + }; + if *actual_ty != ty { + bail!("invalid future handle"); + } + (rep, state) + } + }) +} + +fn waitable_state(ty: TableIndex, state: StreamFutureState) -> WaitableState { + match ty { + TableIndex::Stream(ty) => WaitableState::Stream(ty, state), + TableIndex::Future(ty) => WaitableState::Future(ty, state), + } +} + +fn accept( + values: Vec, + mut offset: usize, + transmit_id: TableId, + tx: oneshot::Sender<()>, +) -> impl FnOnce(Reader) -> Result + Send + Sync + 'static { + move |reader| { + let count = match reader { + Reader::Guest { + lower: + RawLowerContext { + store, + options, + types, + instance, + }, + ty, + address, + count, + } => { + let mut store = unsafe { StoreContextMut::(&mut *store.cast()) }; + let lower = &mut unsafe { + LowerContext::new(store.as_context_mut(), options, types, instance) + }; + let count = values.len().min(usize::try_from(count).unwrap()); + if let Some(ty) = payload(ty, types) { + T::store_list(lower, ty, address, &values[offset..][..count])?; + } + offset += count; + + if offset < values.len() { + let transmit = store.concurrent_state().table.get_mut(transmit_id)?; + assert!(matches!(&transmit.write, WriteState::Open)); + + transmit.write = WriteState::HostReady { + accept: Box::new(accept::(values, offset, transmit_id, tx)), + close: false, + }; + } + + count + } + Reader::Host { accept } => { + assert!(offset == 0); // todo: do we need to handle offset != 0? + let count = values.len(); + accept(Box::new(values))?; + + count + } + Reader::None => 0, + }; + + Ok(count) + } +} + +fn host_write>( + mut store: S, + rep: u32, + values: Vec, + mut close: bool, +) -> Result> { + let mut store = store.as_context_mut(); + let (tx, rx) = oneshot::channel(); + let transmit_id = TableId::::new(rep); + let mut offset = 0; + + loop { + let transmit = store + .concurrent_state() + .table + .get_mut(transmit_id) + .with_context(|| rep.to_string())?; + let new_state = if let ReadState::Closed = &transmit.read { + ReadState::Closed + } else { + ReadState::Open + }; + + match mem::replace(&mut transmit.read, new_state) { + ReadState::Open => { + assert!(matches!(&transmit.write, WriteState::Open)); + + transmit.write = WriteState::HostReady { + accept: Box::new(accept::(values, offset, transmit_id, tx)), + close, + }; + close = false; + } + + ReadState::GuestReady { + ty, + flat_abi: _, + options, + address, + count, + instance, + handle, + caller, + } => unsafe { + let types = (*instance.as_ptr()).component_types(); + let lower = &mut LowerContext::new( + store.as_context_mut(), + &options, + types, + instance.as_ptr(), + ); + let count = values.len().min(count); + if let Some(ty) = payload(ty, types) { + T::store_list(lower, ty, address, &values[offset..][..count])?; + } + offset += count; + + log::trace!( + "remove read child of {}: {}", + caller.rep(), + transmit_id.rep() + ); + store + .concurrent_state() + .table + .remove_child(transmit_id, caller)?; + + *get_mut_by_index(&mut *instance.as_ptr(), ty, handle)?.1 = StreamFutureState::Read; + + push_event( + store.as_context_mut(), + transmit_id.rep(), + match ty { + TableIndex::Future(_) => events::EVENT_FUTURE_READ, + TableIndex::Stream(_) => events::EVENT_STREAM_READ, + }, + count, + caller, + ); + + if offset < values.len() { + continue; + } + }, + + ReadState::HostReady { accept } => { + accept(Writer::Host { + values: Box::new(values), + })?; + } + + ReadState::Closed => {} + } + + if close { + host_close_writer(store, rep)?; + } + + break Ok(rx); + } +} + +pub fn host_read>( + mut store: S, + rep: u32, +) -> Result>>> { + let mut store = store.as_context_mut(); + let (tx, rx) = oneshot::channel(); + let transmit_id = TableId::::new(rep); + let transmit = store + .concurrent_state() + .table + .get_mut(transmit_id) + .with_context(|| rep.to_string())?; + let new_state = if let WriteState::Closed = &transmit.write { + WriteState::Closed + } else { + WriteState::Open + }; + + match mem::replace(&mut transmit.write, new_state) { + WriteState::Open => { + assert!(matches!(&transmit.read, ReadState::Open)); + + transmit.read = ReadState::HostReady { + accept: Box::new(move |writer| { + Ok(match writer { + Writer::Guest { + lift, + ty, + address, + count, + } => { + _ = tx.send( + ty.map(|ty| { + let list = &WasmList::new(address, count, lift, ty)?; + T::load_list(lift, list) + }) + .transpose()?, + ); + count + } + Writer::Host { values } => { + let values = *values + .downcast::>() + .map_err(|_| anyhow!("transmit type mismatch"))?; + let count = values.len(); + _ = tx.send(Some(values)); + count + } + Writer::None => 0, + }) + }), + }; + } + + WriteState::GuestReady { + ty, + flat_abi: _, + options, + address, + count, + instance, + handle, + caller, + close, + } => unsafe { + let types = (*instance.as_ptr()).component_types(); + let lift = &mut LiftContext::new(store.0, &options, types, instance.as_ptr()); + _ = tx.send( + payload(ty, types) + .map(|ty| { + let list = &WasmList::new(address, count, lift, ty)?; + T::load_list(lift, list) + }) + .transpose()?, + ); + + log::trace!( + "remove write child of {}: {}", + caller.rep(), + transmit_id.rep() + ); + store + .concurrent_state() + .table + .remove_child(transmit_id, caller)?; + + if close { + store.concurrent_state().table.get_mut(transmit_id)?.write = WriteState::Closed; + } else { + *get_mut_by_index(&mut *instance.as_ptr(), ty, handle)?.1 = + StreamFutureState::Write; + } + + push_event( + store, + transmit_id.rep(), + match ty { + TableIndex::Future(_) => events::EVENT_FUTURE_WRITE, + TableIndex::Stream(_) => events::EVENT_STREAM_WRITE, + }, + count, + caller, + ); + }, + + WriteState::HostReady { accept, close } => { + accept(Reader::Host { + accept: Box::new(move |any| { + _ = tx.send(Some( + *any.downcast() + .map_err(|_| anyhow!("transmit type mismatch"))?, + )); + Ok(()) + }), + })?; + + if close { + store.concurrent_state().table.get_mut(transmit_id)?.write = WriteState::Closed; + } + } + + WriteState::Closed => { + host_close_reader(store, rep)?; + } + } + + Ok(rx) +} + +fn host_cancel_write>(mut store: S, rep: u32) -> Result { + let mut store = store.as_context_mut(); + let transmit_id = TableId::::new(rep); + let transmit = store.concurrent_state().table.get_mut(transmit_id)?; + + match &transmit.write { + WriteState::GuestReady { caller, .. } => { + let caller = *caller; + transmit.write = WriteState::Open; + store + .concurrent_state() + .table + .remove_child(transmit_id, caller)?; + } + + WriteState::HostReady { .. } => { + transmit.write = WriteState::Open; + } + + WriteState::Open | WriteState::Closed => { + bail!("stream or future write canceled when no write is pending") + } + } + + log::trace!("canceled write {rep}"); + + Ok(0) +} + +fn host_cancel_read>(mut store: S, rep: u32) -> Result { + let mut store = store.as_context_mut(); + let transmit_id = TableId::::new(rep); + let transmit = store.concurrent_state().table.get_mut(transmit_id)?; + + match &transmit.read { + ReadState::GuestReady { caller, .. } => { + let caller = *caller; + transmit.read = ReadState::Open; + store + .concurrent_state() + .table + .remove_child(transmit_id, caller)?; + } + + ReadState::HostReady { .. } => { + transmit.read = ReadState::Open; + } + + ReadState::Open | ReadState::Closed => { + bail!("stream or future read canceled when no read is pending") + } + } + + log::trace!("canceled read {rep}"); + + Ok(0) +} + +fn host_close_writer>(mut store: S, rep: u32) -> Result<()> { + let mut store = store.as_context_mut(); + let transmit_id = TableId::::new(rep); + let transmit = store.concurrent_state().table.get_mut(transmit_id)?; + + match &mut transmit.write { + WriteState::GuestReady { close, .. } => { + *close = true; + } + + WriteState::HostReady { close, .. } => { + *close = true; + } + + v @ WriteState::Open => { + *v = WriteState::Closed; + } + + WriteState::Closed => unreachable!(), + } + + let new_state = if let ReadState::Closed = &transmit.read { + ReadState::Closed + } else { + ReadState::Open + }; + + match mem::replace(&mut transmit.read, new_state) { + ReadState::GuestReady { + ty, + instance, + handle, + caller, + .. + } => unsafe { + push_event( + store, + transmit_id.rep(), + match ty { + TableIndex::Future(_) => events::EVENT_FUTURE_READ, + TableIndex::Stream(_) => events::EVENT_STREAM_READ, + }, + CLOSED, + caller, + ); + + *get_mut_by_index(&mut *instance.as_ptr(), ty, handle)?.1 = StreamFutureState::Read; + }, + + ReadState::HostReady { accept } => { + accept(Writer::None)?; + + host_close_reader(store, rep)?; + } + + ReadState::Open => {} + + ReadState::Closed => { + log::trace!("host_close_writer delete {}", transmit_id.rep()); + store.concurrent_state().table.delete(transmit_id)?; + } + } + Ok(()) +} + +fn host_close_reader>(mut store: S, rep: u32) -> Result<()> { + let mut store = store.as_context_mut(); + let transmit_id = TableId::::new(rep); + let transmit = store.concurrent_state().table.get_mut(transmit_id)?; + + transmit.read = ReadState::Closed; + + let new_state = if let WriteState::Closed = &transmit.write { + WriteState::Closed + } else { + WriteState::Open + }; + + match mem::replace(&mut transmit.write, new_state) { + WriteState::GuestReady { + ty, + instance, + handle, + close, + caller, + .. + } => unsafe { + push_event( + store.as_context_mut(), + transmit_id.rep(), + match ty { + TableIndex::Future(_) => events::EVENT_FUTURE_WRITE, + TableIndex::Stream(_) => events::EVENT_STREAM_WRITE, + }, + CLOSED, + caller, + ); + + if close { + store.concurrent_state().table.delete(transmit_id)?; + } else { + *get_mut_by_index(&mut *instance.as_ptr(), ty, handle)?.1 = + StreamFutureState::Write; + } + }, + + WriteState::HostReady { accept, close } => { + accept(Reader::None)?; + + if close { + store.concurrent_state().table.delete(transmit_id)?; + } + } + + WriteState::Open => {} + + WriteState::Closed => { + log::trace!("host_close_reader delete {}", transmit_id.rep()); + store.concurrent_state().table.delete(transmit_id)?; + } + } + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct FlatAbi { + size: u32, + align: u32, +} + +/// Represents the writable end of a Component Model `future`. +pub struct FutureWriter { + rep: u32, + _phantom: PhantomData, +} + +impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write>(self, store: S, value: T) -> Result> + where + T: func::Lower + Send + Sync + 'static, + { + Ok(Promise(Box::pin( + host_write(store, self.rep, vec![value], true)?.map(drop), + ))) + } + + /// Close this object without writing a value. + /// + /// If this object is dropped without calling either this method or `write`, + /// any read on the readable end will remain pending forever. + pub fn close>(self, store: S) -> Result<()> { + host_close_writer(store, self.rep) + } +} + +/// Represents the readable end of a Component Model `future`. +pub struct FutureReader { + rep: u32, + _phantom: PhantomData, +} + +impl FutureReader { + pub(crate) fn new(rep: u32) -> Self { + Self { + rep, + _phantom: PhantomData, + } + } + + /// Read the value from this `future`. + pub fn read>(self, store: S) -> Result>> + where + T: func::Lift + Sync + Send + 'static, + { + Ok(Promise(Box::pin(host_read(store, self.rep)?.map(|v| { + v.ok() + .and_then(|v| v.map(|v| v.into_iter().next().unwrap())) + })))) + } + + /// Convert this `FutureReader` into a [`Val`]. + pub fn into_val(self) -> Val { + Val::Future(FutureAny(self.rep)) + } + + /// Attempt to convert the specified [`Val`] to a `FutureReader`. + pub fn from_val>(mut store: S, value: &Val) -> Result { + let Val::Future(FutureAny(rep)) = value else { + bail!("expected `future`; got `{}`", value.desc()); + }; + store + .as_context_mut() + .concurrent_state() + .table + .get(TableId::::new(*rep))?; + Ok(Self::new(*rep)) + } + + fn lower_to_index(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result { + match ty { + InterfaceType::Future(dst) => { + state_table(unsafe { &mut *cx.instance }, TableIndex::Future(dst)).insert( + self.rep, + WaitableState::Future(dst, StreamFutureState::Read), + ) + } + _ => func::bad_type_info(), + } + } + + fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result { + match ty { + InterfaceType::Future(src) => { + let state_table = + state_table(unsafe { &mut *cx.instance }, TableIndex::Future(src)); + let (rep, state) = + get_mut_by_index_from(state_table, TableIndex::Future(src), index)?; + + match state { + StreamFutureState::Local => { + *state = StreamFutureState::Write; + } + StreamFutureState::Read => { + state_table.remove_by_index(index)?; + } + StreamFutureState::Write => bail!("cannot transfer write end of future"), + StreamFutureState::Busy => bail!("cannot transfer busy future"), + } + + Ok(Self { + rep, + _phantom: PhantomData, + }) + } + _ => func::bad_type_info(), + } + } + + /// Close this object without reading the value. + /// + /// If this object is dropped without calling either this method or `read`, + /// any write on the writable end will remain pending forever. + pub fn close>(self, store: S) -> Result<()> { + host_close_reader(store, self.rep) + } +} + +unsafe impl func::ComponentType for FutureReader { + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4; + + type Lower = ::Lower; + + fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> { + match ty { + InterfaceType::Future(_) => Ok(()), + other => bail!("expected `future`, found `{}`", func::desc(other)), + } + } +} + +unsafe impl func::Lower for FutureReader { + fn lower( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + dst: &mut MaybeUninit, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .lower(cx, InterfaceType::U32, dst) + } + + fn store( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + offset: usize, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .store(cx, InterfaceType::U32, offset) + } +} + +unsafe impl func::Lift for FutureReader { + fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result { + let index = u32::lift(cx, InterfaceType::U32, src)?; + Self::lift_from_index(cx, ty, index) + } + + fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result { + let index = u32::load(cx, InterfaceType::U32, bytes)?; + Self::lift_from_index(cx, ty, index) + } +} + +/// Create a new Component Model `future` as pair of writable and readable ends, +/// the latter of which may be passed to guest code. +pub fn future>( + mut store: S, +) -> Result<(FutureWriter, FutureReader)> { + let mut store = store.as_context_mut(); + let transmit = store.concurrent_state().table.push(TransmitState { + read: ReadState::Open, + write: WriteState::Open, + })?; + + Ok(( + FutureWriter { + rep: transmit.rep(), + _phantom: PhantomData, + }, + FutureReader { + rep: transmit.rep(), + _phantom: PhantomData, + }, + )) +} + +/// Represents the writable end of a Component Model `stream`. +pub struct StreamWriter { + rep: u32, + _phantom: PhantomData, +} + +impl StreamWriter { + /// Write the specified values to the `stream`. + pub fn write>( + self, + store: S, + values: Vec, + ) -> Result>> + where + T: func::Lower + Send + Sync + 'static, + { + Ok(Promise(Box::pin( + host_write(store, self.rep, values, false)?.map(move |_| self), + ))) + } + + /// Close this object without writing any more values. + /// + /// If this object is dropped without calling this method, any read on the + /// readable end will remain pending forever. + pub fn close>(self, store: S) -> Result<()> { + host_close_writer(store, self.rep) + } +} + +/// Represents the readable end of a Component Model `stream`. +pub struct StreamReader { + rep: u32, + _phantom: PhantomData, +} + +impl StreamReader { + pub(crate) fn new(rep: u32) -> Self { + Self { + rep, + _phantom: PhantomData, + } + } + + /// Read the next values (if any) from this `stream`. + pub fn read>( + self, + store: S, + ) -> Result, Vec)>>> + where + T: func::Lift + Sync + Send + 'static, + { + Ok(Promise(Box::pin( + host_read(store, self.rep)?.map(move |v| v.ok().and_then(|v| v.map(|v| (self, v)))), + ))) + } + + /// Convert this `StreamReader` into a [`Val`]. + pub fn into_val(self) -> Val { + Val::Stream(StreamAny(self.rep)) + } + + /// Attempt to convert the specified [`Val`] to a `StreamReader`. + pub fn from_val>(mut store: S, value: &Val) -> Result { + let Val::Stream(StreamAny(rep)) = value else { + bail!("expected `stream`; got `{}`", value.desc()); + }; + store + .as_context_mut() + .concurrent_state() + .table + .get(TableId::::new(*rep))?; + Ok(Self::new(*rep)) + } + + fn lower_to_index(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result { + match ty { + InterfaceType::Stream(dst) => { + state_table(unsafe { &mut *cx.instance }, TableIndex::Stream(dst)).insert( + self.rep, + WaitableState::Stream(dst, StreamFutureState::Read), + ) + } + _ => func::bad_type_info(), + } + } + + fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result { + match ty { + InterfaceType::Stream(src) => { + let state_table = + state_table(unsafe { &mut *cx.instance }, TableIndex::Stream(src)); + let (rep, state) = + get_mut_by_index_from(state_table, TableIndex::Stream(src), index)?; + + match state { + StreamFutureState::Local => { + *state = StreamFutureState::Write; + } + StreamFutureState::Read => { + state_table.remove_by_index(index)?; + } + StreamFutureState::Write => bail!("cannot transfer write end of stream"), + StreamFutureState::Busy => bail!("cannot transfer busy stream"), + } + + Ok(Self { + rep, + _phantom: PhantomData, + }) + } + _ => func::bad_type_info(), + } + } + + /// Close this object without reading any more values. + /// + /// If the object is dropped without either calling this method or reading + /// until the end of the stream, any write on the writable end will remain + /// pending forever. + pub fn close>(self, store: S) -> Result<()> { + host_close_reader(store, self.rep) + } +} + +unsafe impl func::ComponentType for StreamReader { + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4; + + type Lower = ::Lower; + + fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> { + match ty { + InterfaceType::Stream(_) => Ok(()), + other => bail!("expected `stream`, found `{}`", func::desc(other)), + } + } +} + +unsafe impl func::Lower for StreamReader { + fn lower( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + dst: &mut MaybeUninit, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .lower(cx, InterfaceType::U32, dst) + } + + fn store( + &self, + cx: &mut LowerContext<'_, U>, + ty: InterfaceType, + offset: usize, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .store(cx, InterfaceType::U32, offset) + } +} + +unsafe impl func::Lift for StreamReader { + fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result { + let index = u32::lift(cx, InterfaceType::U32, src)?; + Self::lift_from_index(cx, ty, index) + } + + fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result { + let index = u32::load(cx, InterfaceType::U32, bytes)?; + Self::lift_from_index(cx, ty, index) + } +} + +/// Create a new Component Model `stream` as pair of writable and readable ends, +/// the latter of which may be passed to guest code. +pub fn stream>( + mut store: S, +) -> Result<(StreamWriter, StreamReader)> { + let mut store = store.as_context_mut(); + let transmit = store.concurrent_state().table.push(TransmitState { + read: ReadState::Open, + write: WriteState::Open, + })?; + + Ok(( + StreamWriter { + rep: transmit.rep(), + _phantom: PhantomData, + }, + StreamReader { + rep: transmit.rep(), + _phantom: PhantomData, + }, + )) +} + +/// Represents a Component Model `error-context`. +pub struct ErrorContext { + rep: u32, +} + +impl ErrorContext { + pub(crate) fn new(rep: u32) -> Self { + Self { rep } + } + + /// Convert this `ErrorContext` into a [`Val`]. + pub fn into_val(self) -> Val { + Val::ErrorContext(ErrorContextAny(self.rep)) + } + + /// Attempt to convert the specified [`Val`] to a `ErrorContext`. + pub fn from_val>(_: S, value: &Val) -> Result { + let Val::ErrorContext(ErrorContextAny(rep)) = value else { + bail!("expected `error-context`; got `{}`", value.desc()); + }; + Ok(Self::new(*rep)) + } + + fn lower_to_index(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType) -> Result { + match ty { + InterfaceType::ErrorContext(dst) => { + let dst = unsafe { &mut (*cx.instance).component_error_context_tables()[dst] }; + + if let Some((dst_idx, dst_state)) = dst.get_mut_by_rep(self.rep) { + *dst_state += 1; + Ok(dst_idx) + } else { + dst.insert(self.rep, 1) + } + } + _ => func::bad_type_info(), + } + } + + fn lift_from_index(cx: &mut LiftContext<'_>, ty: InterfaceType, index: u32) -> Result { + match ty { + InterfaceType::ErrorContext(src) => { + let (rep, _) = unsafe { + (*cx.instance).component_error_context_tables()[src].get_mut_by_index(index)? + }; + + Ok(Self { rep }) + } + _ => func::bad_type_info(), + } + } +} + +unsafe impl func::ComponentType for ErrorContext { + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4; + + type Lower = ::Lower; + + fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> { + match ty { + InterfaceType::ErrorContext(_) => Ok(()), + other => bail!("expected `error`, found `{}`", func::desc(other)), + } + } +} + +unsafe impl func::Lower for ErrorContext { + fn lower( + &self, + cx: &mut LowerContext<'_, T>, + ty: InterfaceType, + dst: &mut MaybeUninit, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .lower(cx, InterfaceType::U32, dst) + } + + fn store( + &self, + cx: &mut LowerContext<'_, T>, + ty: InterfaceType, + offset: usize, + ) -> Result<()> { + self.lower_to_index(cx, ty)? + .store(cx, InterfaceType::U32, offset) + } +} + +unsafe impl func::Lift for ErrorContext { + fn lift(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result { + let index = u32::lift(cx, InterfaceType::U32, src)?; + Self::lift_from_index(cx, ty, index) + } + + fn load(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result { + let index = u32::load(cx, InterfaceType::U32, bytes)?; + Self::lift_from_index(cx, ty, index) + } +} + +pub(super) struct TransmitState { + write: WriteState, + read: ReadState, +} + +enum WriteState { + Open, + GuestReady { + ty: TableIndex, + flat_abi: Option, + options: Options, + address: usize, + count: usize, + instance: SendSyncPtr, + handle: u32, + caller: TableId, + close: bool, + }, + HostReady { + accept: Box Result + Send + Sync>, + close: bool, + }, + Closed, +} + +enum ReadState { + Open, + GuestReady { + ty: TableIndex, + flat_abi: Option, + options: Options, + address: usize, + count: usize, + instance: SendSyncPtr, + handle: u32, + caller: TableId, + }, + HostReady { + accept: Box Result + Send + Sync>, + }, + Closed, +} + +enum Writer<'a> { + Guest { + lift: &'a mut LiftContext<'a>, + ty: Option, + address: usize, + count: usize, + }, + Host { + values: Box, + }, + None, +} + +struct RawLowerContext<'a> { + store: *mut dyn VMStore, + options: &'a Options, + types: &'a Arc, + instance: *mut ComponentInstance, +} + +enum Reader<'a> { + Guest { + lower: RawLowerContext<'a>, + ty: TableIndex, + address: usize, + count: usize, + }, + Host { + accept: Box) -> Result<()>>, + }, + None, +} + +fn guest_new(vmctx: *mut VMOpaqueContext, ty: TableIndex) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let transmit = cx.concurrent_state().table.push(TransmitState { + read: ReadState::Open, + write: WriteState::Open, + })?; + state_table(&mut *instance, ty) + .insert(transmit.rep(), waitable_state(ty, StreamFutureState::Local)) + }) + } +} + +unsafe fn copy( + mut cx: StoreContextMut<'_, T>, + types: &Arc, + instance: *mut ComponentInstance, + flat_abi: Option, + write_ty: TableIndex, + write_options: &Options, + write_address: usize, + read_ty: TableIndex, + read_options: &Options, + read_address: usize, + count: usize, + rep: u32, +) -> Result<()> { + match (write_ty, read_ty) { + (TableIndex::Future(write_ty), TableIndex::Future(read_ty)) => { + assert_eq!(count, 1); + + let val = types[types[write_ty].ty] + .payload + .map(|ty| { + let lift = &mut LiftContext::new(cx.0, write_options, types, instance); + Val::load( + lift, + ty, + &lift.memory()[usize::try_from(write_address).unwrap()..] + [..usize::try_from(types.canonical_abi(&ty).size32).unwrap()], + ) + }) + .transpose()?; + + let mut lower = LowerContext::new(cx.as_context_mut(), read_options, types, instance); + if let Some(val) = val { + val.store( + &mut lower, + types[types[read_ty].ty].payload.unwrap(), + usize::try_from(read_address).unwrap(), + )?; + } + } + (TableIndex::Stream(write_ty), TableIndex::Stream(read_ty)) => { + let lift = &mut LiftContext::new(cx.0, write_options, types, instance); + if let Some(flat_abi) = flat_abi { + // Fast path memcpy for "flat" (i.e. no pointers or handles) payloads: + let length_in_bytes = usize::try_from(flat_abi.size).unwrap() * count; + + { + let src = + write_options.memory(cx.0)[write_address..][..length_in_bytes].as_ptr(); + let dst = read_options.memory_mut(cx.0)[read_address..][..length_in_bytes] + .as_mut_ptr(); + src.copy_to(dst, length_in_bytes); + } + } else { + let ty = types[types[write_ty].ty].payload; + let abi = lift.types.canonical_abi(&ty); + let size = usize::try_from(abi.size32).unwrap(); + let values = (0..count) + .map(|index| { + Val::load( + lift, + ty, + &lift.memory()[write_address + (index * size)..][..size], + ) + }) + .collect::>>()?; + + log::trace!("copy values {values:?} for {rep}"); + + let lower = + &mut LowerContext::new(cx.as_context_mut(), read_options, types, instance); + let mut ptr = read_address; + let ty = types[types[read_ty].ty].payload; + let abi = lower.types.canonical_abi(&ty); + let size = usize::try_from(abi.size32).unwrap(); + for value in values { + value.store(lower, ty, ptr)?; + ptr += size + } + } + } + _ => unreachable!(), + } + + Ok(()) +} + +fn guest_write( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TableIndex, + flat_abi: Option, + handle: u32, + address: u32, + count: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + let address = usize::try_from(address).unwrap(); + let count = usize::try_from(count).unwrap(); + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let options = Options::new( + cx.0.id(), + NonNull::new(memory), + NonNull::new(realloc), + StringEncoding::from_u8(string_encoding).unwrap(), + true, + None, + ); + let types = (*instance).component_types(); + let (rep, state) = get_mut_by_index(&mut *instance, ty, handle)?; + let StreamFutureState::Write = *state else { + bail!("invalid handle"); + }; + *state = StreamFutureState::Busy; + let transmit_id = TableId::::new(rep); + let transmit = cx.concurrent_state().table.get_mut(transmit_id)?; + let new_state = if let ReadState::Closed = &transmit.read { + ReadState::Closed + } else { + ReadState::Open + }; + + let result = match mem::replace(&mut transmit.read, new_state) { + ReadState::GuestReady { + ty: read_ty, + flat_abi: read_flat_abi, + options: read_options, + address: read_address, + count: read_count, + instance: _, + handle: read_handle, + caller: read_caller, + } => { + assert_eq!(flat_abi, read_flat_abi); + + let count = count.min(read_count); + + copy( + cx.as_context_mut(), + types, + instance, + flat_abi, + ty, + &options, + address, + read_ty, + &read_options, + read_address, + count, + rep, + )?; + + log::trace!( + "remove read child of {}: {}", + read_caller.rep(), + transmit_id.rep() + ); + cx.concurrent_state() + .table + .remove_child(transmit_id, read_caller)?; + + *get_mut_by_index(&mut *instance, read_ty, read_handle)?.1 = + StreamFutureState::Read; + + push_event( + cx, + transmit_id.rep(), + match read_ty { + TableIndex::Future(_) => events::EVENT_FUTURE_READ, + TableIndex::Stream(_) => events::EVENT_STREAM_READ, + }, + count, + read_caller, + ); + + count + } + + ReadState::HostReady { accept } => { + let lift = &mut LiftContext::new(cx.0, &options, types, instance); + accept(Writer::Guest { + lift, + ty: payload(ty, types), + address, + count, + })? + } + + ReadState::Open => { + assert!(matches!(&transmit.write, WriteState::Open)); + + let caller = cx.concurrent_state().guest_task.unwrap(); + log::trace!( + "add write {} child of {}: {}", + match ty { + TableIndex::Future(_) => "future", + TableIndex::Stream(_) => "stream", + }, + caller.rep(), + transmit_id.rep() + ); + cx.concurrent_state().table.add_child(transmit_id, caller)?; + + let transmit = cx.concurrent_state().table.get_mut(transmit_id)?; + transmit.write = WriteState::GuestReady { + ty, + flat_abi, + options, + address: usize::try_from(address).unwrap(), + count: usize::try_from(count).unwrap(), + instance: SendSyncPtr::new(NonNull::new(instance).unwrap()), + handle, + caller, + close: false, + }; + + BLOCKED + } + + ReadState::Closed => CLOSED, + }; + + if result != BLOCKED { + *get_mut_by_index(&mut *instance, ty, handle)?.1 = StreamFutureState::Write; + } + + Ok(u32::try_from(result).unwrap()) + }) + } +} + +fn guest_read( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TableIndex, + flat_abi: Option, + handle: u32, + address: u32, + count: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + let address = usize::try_from(address).unwrap(); + let count = usize::try_from(count).unwrap(); + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let mut cx = StoreContextMut::(&mut *(*instance).store().cast()); + let options = Options::new( + cx.0.id(), + NonNull::new(memory), + NonNull::new(realloc), + StringEncoding::from_u8(string_encoding).unwrap(), + true, + None, + ); + let types = (*instance).component_types(); + let (rep, state) = get_mut_by_index(&mut *instance, ty, handle)?; + let StreamFutureState::Read = *state else { + bail!("invalid handle"); + }; + *state = StreamFutureState::Busy; + let transmit_id = TableId::::new(rep); + let transmit = cx.concurrent_state().table.get_mut(transmit_id)?; + let new_state = if let WriteState::Closed = &transmit.write { + WriteState::Closed + } else { + WriteState::Open + }; + + let result = match mem::replace(&mut transmit.write, new_state) { + WriteState::GuestReady { + ty: write_ty, + flat_abi: write_flat_abi, + options: write_options, + address: write_address, + count: write_count, + instance: _, + handle: write_handle, + caller: write_caller, + close, + } => { + assert_eq!(flat_abi, write_flat_abi); + + let count = count.min(write_count); + + copy( + cx.as_context_mut(), + types, + instance, + flat_abi, + write_ty, + &write_options, + write_address, + ty, + &options, + address, + count, + rep, + )?; + + log::trace!( + "remove write child of {}: {}", + write_caller.rep(), + transmit_id.rep() + ); + cx.concurrent_state() + .table + .remove_child(transmit_id, write_caller)?; + + if close { + cx.concurrent_state().table.get_mut(transmit_id)?.write = + WriteState::Closed; + } else { + *get_mut_by_index(&mut *instance, write_ty, write_handle)?.1 = + StreamFutureState::Write; + } + + push_event( + cx, + transmit_id.rep(), + match write_ty { + TableIndex::Future(_) => events::EVENT_FUTURE_WRITE, + TableIndex::Stream(_) => events::EVENT_STREAM_WRITE, + }, + count, + write_caller, + ); + + count + } + + WriteState::HostReady { accept, close } => { + let count = accept(Reader::Guest { + lower: RawLowerContext { + store: cx.0.traitobj(), + options: &options, + types, + instance, + }, + ty, + address: usize::try_from(address).unwrap(), + count, + })?; + + if close { + cx.concurrent_state().table.get_mut(transmit_id)?.write = + WriteState::Closed; + } + + count + } + + WriteState::Open => { + assert!(matches!(&transmit.read, ReadState::Open)); + + let caller = cx.concurrent_state().guest_task.unwrap(); + log::trace!( + "add read {} child of {}: {}", + match ty { + TableIndex::Future(_) => "future", + TableIndex::Stream(_) => "stream", + }, + caller.rep(), + transmit_id.rep() + ); + cx.concurrent_state().table.add_child(transmit_id, caller)?; + + let transmit = cx.concurrent_state().table.get_mut(transmit_id)?; + transmit.read = ReadState::GuestReady { + ty, + flat_abi, + options, + address: usize::try_from(address).unwrap(), + count: usize::try_from(count).unwrap(), + instance: SendSyncPtr::new(NonNull::new(instance).unwrap()), + handle, + caller, + }; + + BLOCKED + } + + WriteState::Closed => CLOSED, + }; + + if result != BLOCKED { + *get_mut_by_index(&mut *instance, ty, handle)?.1 = StreamFutureState::Read; + } + + Ok(u32::try_from(result).unwrap()) + }) + } +} + +fn guest_cancel_write( + vmctx: *mut VMOpaqueContext, + ty: TableIndex, + writer: u32, + _async_: bool, +) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let cx = StoreContextMut::(&mut *(*instance).store().cast()); + let (rep, WaitableState::Stream(_, state) | WaitableState::Future(_, state)) = + state_table(&mut *instance, ty).get_mut_by_index(writer)? + else { + bail!("invalid stream or future handle"); + }; + match state { + StreamFutureState::Local | StreamFutureState::Write => { + bail!("stream or future write canceled when no write is pending") + } + StreamFutureState::Read => { + bail!("passed read end to `{{stream|future}}.cancel-write`") + } + StreamFutureState::Busy => { + *state = StreamFutureState::Write; + } + } + host_cancel_write(cx, rep) + }) + } +} + +fn guest_cancel_read( + vmctx: *mut VMOpaqueContext, + ty: TableIndex, + reader: u32, + _async_: bool, +) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let cx = StoreContextMut::(&mut *(*instance).store().cast()); + let (rep, WaitableState::Stream(_, state) | WaitableState::Future(_, state)) = + state_table(&mut *instance, ty).get_mut_by_index(reader)? + else { + bail!("invalid stream or future handle"); + }; + match state { + StreamFutureState::Local | StreamFutureState::Read => { + bail!("stream or future read canceled when no read is pending") + } + StreamFutureState::Write => { + bail!("passed write end to `{{stream|future}}.cancel-read`") + } + StreamFutureState::Busy => { + *state = StreamFutureState::Read; + } + } + host_cancel_read(cx, rep) + }) + } +} + +fn guest_close_writable( + vmctx: *mut VMOpaqueContext, + ty: TableIndex, + writer: u32, + error: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(vmctx, || { + if error != 0 { + bail!("todo: closing writable streams and futures with errors not yet implemented"); + } + + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let cx = StoreContextMut::(&mut *(*instance).store().cast()); + let (rep, WaitableState::Stream(_, state) | WaitableState::Future(_, state)) = + state_table(&mut *instance, ty).remove_by_index(writer)? + else { + bail!("invalid stream or future handle"); + }; + match state { + StreamFutureState::Local | StreamFutureState::Write => {} + StreamFutureState::Read => { + bail!("passed read end to `{{stream|future}}.close-writable`") + } + StreamFutureState::Busy => bail!("cannot drop busy stream or future"), + } + host_close_writer(cx, rep) + }) + } +} + +fn guest_close_readable(vmctx: *mut VMOpaqueContext, ty: TableIndex, reader: u32) -> bool { + unsafe { + call_host_and_handle_result::(vmctx, || { + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let cx = StoreContextMut::(&mut *(*instance).store().cast()); + let (rep, WaitableState::Stream(_, state) | WaitableState::Future(_, state)) = + state_table(&mut *instance, ty).remove_by_index(reader)? + else { + bail!("invalid stream or future handle"); + }; + match state { + StreamFutureState::Local | StreamFutureState::Read => {} + StreamFutureState::Write => { + bail!("passed write end to `{{stream|future}}.close-readable`") + } + StreamFutureState::Busy => bail!("cannot drop busy stream or future"), + } + host_close_reader(cx, rep) + }) + } +} + +pub(crate) extern "C" fn future_new( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, +) -> u64 { + guest_new::(vmctx, TableIndex::Future(ty)) +} + +pub(crate) extern "C" fn future_write( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeFutureTableIndex, + future: u32, + address: u32, +) -> u64 { + guest_write::( + vmctx, + memory, + realloc, + string_encoding, + TableIndex::Future(ty), + None, + future, + address, + 1, + ) +} + +pub(crate) extern "C" fn future_read( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeFutureTableIndex, + future: u32, + address: u32, +) -> u64 { + guest_read::( + vmctx, + memory, + realloc, + string_encoding, + TableIndex::Future(ty), + None, + future, + address, + 1, + ) +} + +pub(crate) extern "C" fn future_cancel_write( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + async_: bool, + writer: u32, +) -> u64 { + guest_cancel_write::(vmctx, TableIndex::Future(ty), writer, async_) +} + +pub(crate) extern "C" fn future_cancel_read( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + async_: bool, + reader: u32, +) -> u64 { + guest_cancel_read::(vmctx, TableIndex::Future(ty), reader, async_) +} + +pub(crate) extern "C" fn future_close_writable( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + writer: u32, + error: u32, +) -> bool { + guest_close_writable::(vmctx, TableIndex::Future(ty), writer, error) +} + +pub(crate) extern "C" fn future_close_readable( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + reader: u32, +) -> bool { + guest_close_readable::(vmctx, TableIndex::Future(ty), reader) +} + +pub(crate) extern "C" fn stream_new( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, +) -> u64 { + guest_new::(vmctx, TableIndex::Stream(ty)) +} + +pub(crate) extern "C" fn stream_write( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeStreamTableIndex, + stream: u32, + address: u32, + count: u32, +) -> u64 { + guest_write::( + vmctx, + memory, + realloc, + string_encoding, + TableIndex::Stream(ty), + None, + stream, + address, + count, + ) +} + +pub(crate) extern "C" fn stream_read( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeStreamTableIndex, + stream: u32, + address: u32, + count: u32, +) -> u64 { + guest_read::( + vmctx, + memory, + realloc, + string_encoding, + TableIndex::Stream(ty), + None, + stream, + address, + count, + ) +} + +pub(crate) extern "C" fn stream_cancel_write( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + async_: bool, + writer: u32, +) -> u64 { + guest_cancel_write::(vmctx, TableIndex::Stream(ty), writer, async_) +} + +pub(crate) extern "C" fn stream_cancel_read( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + async_: bool, + reader: u32, +) -> u64 { + guest_cancel_read::(vmctx, TableIndex::Stream(ty), reader, async_) +} + +pub(crate) extern "C" fn stream_close_writable( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + writer: u32, + error: u32, +) -> bool { + guest_close_writable::(vmctx, TableIndex::Stream(ty), writer, error) +} + +pub(crate) extern "C" fn stream_close_readable( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + reader: u32, +) -> bool { + guest_close_readable::(vmctx, TableIndex::Stream(ty), reader) +} + +pub(crate) extern "C" fn flat_stream_write( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + ty: TypeStreamTableIndex, + payload_size: u32, + payload_align: u32, + stream: u32, + address: u32, + count: u32, +) -> u64 { + guest_write::( + vmctx, + memory, + realloc, + StringEncoding::Utf8 as u8, + TableIndex::Stream(ty), + Some(FlatAbi { + size: payload_size, + align: payload_align, + }), + stream, + address, + count, + ) +} + +pub(crate) extern "C" fn flat_stream_read( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + ty: TypeStreamTableIndex, + payload_size: u32, + payload_align: u32, + stream: u32, + address: u32, + count: u32, +) -> u64 { + guest_read::( + vmctx, + memory, + realloc, + StringEncoding::Utf8 as u8, + TableIndex::Stream(ty), + Some(FlatAbi { + size: payload_size, + align: payload_align, + }), + stream, + address, + count, + ) +} + +pub(crate) extern "C" fn error_context_new( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeErrorContextTableIndex, + address: u32, + count: u32, +) -> u64 { + unsafe { + call_host_and_handle_result::(vmctx, || { + _ = ( + vmctx, + memory, + realloc, + StringEncoding::from_u8(string_encoding).unwrap(), + ty, + address, + count, + ); + bail!("todo: `error.new` not yet implemented"); + }) + } +} + +pub(crate) extern "C" fn error_context_debug_message( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeErrorContextTableIndex, + handle: u32, + address: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(vmctx, || { + _ = ( + vmctx, + memory, + realloc, + StringEncoding::from_u8(string_encoding).unwrap(), + ty, + handle, + address, + ); + bail!("todo: `error.debug-message` not yet implemented"); + }) + } +} + +pub(crate) extern "C" fn error_context_drop( + vmctx: *mut VMOpaqueContext, + ty: TypeErrorContextTableIndex, + error_context: u32, +) -> bool { + unsafe { + call_host_and_handle_result::(vmctx, || { + let cx = VMComponentContext::from_opaque(vmctx); + let instance = (*cx).instance(); + let (_, count) = + (*instance).component_error_context_tables()[ty].get_mut_by_index(error_context)?; + assert!(*count > 0); + *count -= 1; + + if *count == 0 { + (*instance).component_error_context_tables()[ty].remove_by_index(error_context)?; + } + + Ok(()) + }) + } +} diff --git a/crates/wasmtime/src/runtime/component/concurrent/ready_chunks.rs b/crates/wasmtime/src/runtime/component/concurrent/ready_chunks.rs new file mode 100644 index 000000000000..f82bddcee4c7 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent/ready_chunks.rs @@ -0,0 +1,59 @@ +//! Like `futures::stream::ReadyChunks` but without fusing the inner stream. +//! +//! We use this with `FuturesUnordered` which may produce `Poll::Ready(None)` but later produce more elements due +//! to additional futures having been added, so fusing is not appropriate. + +use { + futures::{Stream, StreamExt}, + std::{ + pin::Pin, + task::{Context, Poll}, + vec::Vec, + }, +}; + +pub struct ReadyChunks { + stream: S, + capacity: usize, +} + +impl ReadyChunks { + pub fn new(stream: S, capacity: usize) -> Self { + Self { stream, capacity } + } + + pub fn get_mut(&mut self) -> &mut S { + &mut self.stream + } +} + +impl Stream for ReadyChunks { + type Item = Vec; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut items = Vec::new(); + + loop { + match self.stream.poll_next_unpin(cx) { + Poll::Pending => { + break if items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(items)) + } + } + + Poll::Ready(Some(item)) => { + items.push(item); + if items.len() >= self.capacity { + break Poll::Ready(Some(items)); + } + } + + Poll::Ready(None) => { + break Poll::Ready(if items.is_empty() { None } else { Some(items) }); + } + } + } + } +} diff --git a/crates/wasmtime/src/runtime/component/concurrent/table.rs b/crates/wasmtime/src/runtime/component/concurrent/table.rs new file mode 100644 index 000000000000..a609052244bf --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent/table.rs @@ -0,0 +1,316 @@ +// TODO: This duplicates a lot of resource_table.rs; consider reducing that +// duplication. +// +// The main difference between this and resource_table.rs is that the key type, +// `TableId` implements `Copy`, making them much easier to work with than +// `Resource`. I've also added a `Table::delete_any` function, useful for +// implementing `subtask.drop`. + +use std::{any::Any, boxed::Box, collections::BTreeSet, marker::PhantomData, vec::Vec}; + +pub struct TableId { + rep: u32, + _marker: PhantomData T>, +} + +impl TableId { + pub fn new(rep: u32) -> Self { + Self { + rep, + _marker: PhantomData, + } + } +} + +impl Clone for TableId { + fn clone(&self) -> Self { + Self::new(self.rep) + } +} + +impl Copy for TableId {} + +impl TableId { + pub fn rep(&self) -> u32 { + self.rep + } +} + +#[derive(Debug)] +/// Errors returned by operations on `Table` +pub enum TableError { + /// Table has no free keys + Full, + /// Entry not present in table + NotPresent, + /// Resource present in table, but with a different type + WrongType, + /// Entry cannot be deleted because child entrys exist in the table. + HasChildren, +} + +impl std::fmt::Display for TableError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Full => write!(f, "table has no free keys"), + Self::NotPresent => write!(f, "entry not present"), + Self::WrongType => write!(f, "entry is of another type"), + Self::HasChildren => write!(f, "entry has children"), + } + } +} +impl std::error::Error for TableError {} + +/// The `Table` type maps a `TableId` to its entry. +#[derive(Default)] +pub struct Table { + entries: Vec, + free_head: Option, +} + +enum Entry { + Free { next: Option }, + Occupied { entry: TableEntry }, +} + +impl Entry { + pub fn occupied(&self) -> Option<&TableEntry> { + match self { + Self::Occupied { entry } => Some(entry), + Self::Free { .. } => None, + } + } + + pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> { + match self { + Self::Occupied { entry } => Some(entry), + Self::Free { .. } => None, + } + } +} + +/// This structure tracks parent and child relationships for a given table entry. +/// +/// Parents and children are referred to by table index. We maintain the +/// following invariants to prevent orphans and cycles: +/// * parent can only be assigned on creating the entry. +/// * parent, if some, must exist when creating the entry. +/// * whenever a child is created, its index is added to children. +/// * whenever a child is deleted, its index is removed from children. +/// * an entry with children may not be deleted. +struct TableEntry { + /// The entry in the table + entry: Box, + /// The index of the parent of this entry, if it has one. + parent: Option, + /// The indicies of any children of this entry. + children: BTreeSet, +} + +impl TableEntry { + fn new(entry: Box, parent: Option) -> Self { + Self { + entry, + parent, + children: BTreeSet::new(), + } + } + fn add_child(&mut self, child: u32) { + assert!(self.children.insert(child)); + } + fn remove_child(&mut self, child: u32) { + assert!(self.children.remove(&child)); + } +} + +impl Table { + /// Create an empty table + pub fn new() -> Self { + let mut me = Self { + entries: Vec::new(), + free_head: None, + }; + + // TODO: remove this once we've stopped exposing these indexes to guest code: + me.push(()).unwrap(); + + me + } + + /// Inserts a new entry into this table, returning a corresponding + /// `TableId` which can be used to refer to it after it was inserted. + pub fn push(&mut self, entry: T) -> Result, TableError> { + let idx = self.push_(TableEntry::new(Box::new(entry), None))?; + Ok(TableId::new(idx)) + } + + /// Pop an index off of the free list, if it's not empty. + fn pop_free_list(&mut self) -> Option { + if let Some(ix) = self.free_head { + // Advance free_head to the next entry if one is available. + match &self.entries[ix] { + Entry::Free { next } => self.free_head = *next, + Entry::Occupied { .. } => unreachable!(), + } + Some(ix) + } else { + None + } + } + + /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list. + fn free_entry(&mut self, ix: usize) -> TableEntry { + let entry = match std::mem::replace( + &mut self.entries[ix], + Entry::Free { + next: self.free_head, + }, + ) { + Entry::Occupied { entry } => entry, + Entry::Free { .. } => unreachable!(), + }; + + self.free_head = Some(ix); + + entry + } + + /// Push a new entry into the table, returning its handle. This will prefer to use free entries + /// if they exist, falling back on pushing new entries onto the end of the table. + fn push_(&mut self, e: TableEntry) -> Result { + if let Some(free) = self.pop_free_list() { + self.entries[free] = Entry::Occupied { entry: e }; + Ok(u32::try_from(free).unwrap()) + } else { + let ix = self + .entries + .len() + .try_into() + .map_err(|_| TableError::Full)?; + self.entries.push(Entry::Occupied { entry: e }); + Ok(ix) + } + } + + fn occupied(&self, key: u32) -> Result<&TableEntry, TableError> { + self.entries + .get(key as usize) + .and_then(Entry::occupied) + .ok_or(TableError::NotPresent) + } + + fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, TableError> { + self.entries + .get_mut(key as usize) + .and_then(Entry::occupied_mut) + .ok_or(TableError::NotPresent) + } + + /// Insert a entry at the next available index, and track that it has a + /// parent entry. + /// + /// The parent must exist to create a child. All child entrys must be + /// destroyed before a parent can be destroyed - otherwise [`Table::delete`] + /// will fail with [`TableError::HasChildren`]. + /// + /// Parent-child relationships are tracked inside the table to ensure that a + /// parent is not deleted while it has live children. This allows children + /// to hold "references" to a parent by table index, to avoid needing + /// e.g. an `Arc>` and the associated locking overhead and + /// design issues, such as child existence extending lifetime of parent + /// referent even after parent is destroyed, possibility for deadlocks. + /// + /// Parent-child relationships may not be modified once created. There is no + /// way to observe these relationships through the [`Table`] methods except + /// for erroring on deletion, or the [`std::fmt::Debug`] impl. + pub fn push_child( + &mut self, + entry: T, + parent: TableId, + ) -> Result, TableError> { + let parent = parent.rep(); + self.occupied(parent)?; + let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?; + self.occupied_mut(parent)?.add_child(child); + Ok(TableId::new(child)) + } + + pub fn add_child( + &mut self, + child: TableId, + parent: TableId, + ) -> Result<(), TableError> { + let entry = self.occupied_mut(child.rep())?; + assert!(entry.parent.is_none()); + entry.parent = Some(parent.rep()); + self.occupied_mut(parent.rep())?.add_child(child.rep()); + Ok(()) + } + + pub fn remove_child( + &mut self, + child: TableId, + parent: TableId, + ) -> Result<(), TableError> { + let entry = self.occupied_mut(child.rep())?; + assert_eq!(entry.parent, Some(parent.rep())); + entry.parent = None; + self.occupied_mut(parent.rep())?.remove_child(child.rep()); + Ok(()) + } + + /// Get an immutable reference to a task of a given type at a given index. + /// + /// Multiple shared references can be borrowed at any given time. + pub fn get(&self, key: TableId) -> Result<&T, TableError> { + self.get_(key.rep())? + .downcast_ref() + .ok_or(TableError::WrongType) + } + + fn get_(&self, key: u32) -> Result<&dyn Any, TableError> { + let r = self.occupied(key)?; + Ok(&*r.entry) + } + + /// Get an mutable reference to a task of a given type at a given index. + pub fn get_mut(&mut self, key: TableId) -> Result<&mut T, TableError> { + self.get_mut_(key.rep())? + .downcast_mut() + .ok_or(TableError::WrongType) + } + + pub fn get_mut_(&mut self, key: u32) -> Result<&mut dyn Any, TableError> { + let r = self.occupied_mut(key)?; + Ok(&mut *r.entry) + } + + /// Delete the specified task + pub fn delete(&mut self, key: TableId) -> Result { + self.delete_entry(key.rep())? + .entry + .downcast() + .map(|v| *v) + .map_err(|_| TableError::WrongType) + } + + pub fn delete_any(&mut self, key: u32) -> Result, TableError> { + Ok(self.delete_entry(key)?.entry) + } + + fn delete_entry(&mut self, key: u32) -> Result { + if !self.occupied(key)?.children.is_empty() { + return Err(TableError::HasChildren); + } + let e = self.free_entry(key as usize); + if let Some(parent) = e.parent { + // Remove deleted task from parent's child list. Parent must still + // be present because it cant be deleted while still having + // children: + self.occupied_mut(parent) + .expect("missing parent") + .remove_child(key); + } + Ok(e) + } +} diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs index 9ac5329af7ce..4d6ce6429cab 100644 --- a/crates/wasmtime/src/runtime/component/func.rs +++ b/crates/wasmtime/src/runtime/component/func.rs @@ -15,6 +15,9 @@ use wasmtime_environ::component::{ TypeFuncIndex, TypeTuple, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, }; +#[cfg(feature = "component-model-async")] +use crate::component::concurrent::{self, LiftLowerContext, Promise}; + mod host; mod options; mod typed; @@ -22,6 +25,13 @@ pub use self::host::*; pub use self::options::*; pub use self::typed::*; +#[cfg(feature = "component-model-async")] +type LowerFn = + fn(&mut LowerContext, &Params, InterfaceType, &mut MaybeUninit) -> Result<()>; + +#[cfg(feature = "component-model-async")] +type LiftFn = fn(&mut LiftContext, InterfaceType, &[ValRaw]) -> Result; + #[repr(C)] union ParamsAndResults { params: Params, @@ -36,16 +46,16 @@ union ParamsAndResults { /// [`wasmtime::Func`](crate::Func) it's possible to call functions either /// synchronously or asynchronously and either typed or untyped. #[derive(Copy, Clone, Debug)] -pub struct Func(Stored); +pub struct Func(pub(crate) Stored); #[doc(hidden)] pub struct FuncData { - export: ExportFunction, - ty: TypeFuncIndex, - types: Arc, - options: Options, - instance: Instance, - component_instance: RuntimeComponentInstanceIndex, + pub(crate) export: ExportFunction, + pub(crate) ty: TypeFuncIndex, + pub(crate) types: Arc, + pub(crate) options: Options, + pub(crate) instance: Instance, + pub(crate) component_instance: RuntimeComponentInstanceIndex, post_return: Option, post_return_arg: Option, } @@ -72,7 +82,19 @@ impl Func { ExportFunction { func_ref } }); let component_instance = options.instance; - let options = unsafe { Options::new(store.id(), memory, realloc, options.string_encoding) }; + let callback = options + .callback + .map(|i| data.instance().runtime_callback(i)); + let options = unsafe { + Options::new( + store.id(), + memory, + realloc, + options.string_encoding, + options.async_, + callback, + ) + }; Func(store.store_data_mut().insert(FuncData { export, options, @@ -269,9 +291,9 @@ impl Func { /// Panics if this is called on a function in an asynchronous store. This /// only works with functions defined within a synchronous store. Also /// panics if `store` does not own this function. - pub fn call( + pub fn call( &self, - mut store: impl AsContextMut, + mut store: impl AsContextMut, params: &[Val], results: &mut [Val], ) -> Result<()> { @@ -294,32 +316,103 @@ impl Func { /// only works with functions defined within an asynchronous store. Also /// panics if `store` does not own this function. #[cfg(feature = "async")] - pub async fn call_async( + pub async fn call_async( &self, mut store: impl AsContextMut, params: &[Val], results: &mut [Val], - ) -> Result<()> - where - T: Send, - { - let mut store = store.as_context_mut(); + ) -> Result<()> { + let store = store.as_context_mut(); 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)) + #[cfg(feature = "component-model-async")] + { + let instance = store.0[self.0].component_instance; + // TODO: do we need to return the store here due to the possible + // invalidation of the reference we were passed? + concurrent::on_fiber(store, instance, move |store| { + self.call_impl(store, params, results) + }) .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store + .on_fiber(|store| self.call_impl(store, params, results)) + .await? + } + } + + /// Start concurrent call to this function. + /// + /// Unlike [`Self::call`] and [`Self::call_async`] (both of which require + /// exclusive access to the store until the completion of the call), calls + /// made using this method may run concurrently with other calls to the same + /// instance. + #[cfg(feature = "component-model-async")] + pub async fn call_concurrent( + self, + mut store: impl AsContextMut, + params: Vec, + ) -> Result>> { + let store = store.as_context_mut(); + assert!( + store.0.async_support(), + "cannot use `call_concurrent` when async support is not enabled on the config" + ); + let instance = store.0[self.0].component_instance; + // TODO: do we need to return the store here due to the possible + // invalidation of the reference we were passed? + concurrent::on_fiber(store, instance, move |store| { + self.start_call(store.as_context_mut(), params) + }) + .await? + .0 + } + + #[cfg(feature = "component-model-async")] + fn start_call<'a, T: Send>( + self, + mut store: StoreContextMut<'a, T>, + params: Vec, + ) -> Result>> { + let store = store.as_context_mut(); + + let param_tys = self.params(&store); + if param_tys.len() != params.len() { + bail!( + "expected {} argument(s), got {}", + param_tys.len(), + params.len() + ); + } + + let (lower, lift) = if store.0[self.0].options.async_() { + ( + Self::lower_args_async as LowerFn<_, _, _>, + Self::lift_results_async as LiftFn<_>, + ) + } else { + ( + Self::lower_args_sync as LowerFn<_, _, _>, + Self::lift_results_sync as LiftFn<_>, + ) + }; + + Ok(self.start_call_raw_async(store, params, lower, lift)?.0) } - fn call_impl( + fn call_impl( &self, - mut store: impl AsContextMut, + mut store: impl AsContextMut, params: &[Val], results: &mut [Val], ) -> Result<()> { - let store = &mut store.as_context_mut(); + let store = store.as_context_mut(); let param_tys = self.params(&store); let result_tys = self.results(&store); @@ -333,49 +426,122 @@ impl Func { } if result_tys.len() != results.len() { bail!( - "expected {} results(s), got {}", + "expected {} result(s), got {}", result_tys.len(), results.len() ); } - self.call_raw( - store, - params, - |cx, params, params_ty, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { - let params_ty = match params_ty { - InterfaceType::Tuple(i) => &cx.types[i], - _ => unreachable!(), - }; - if params_ty.abi.flat_count(MAX_FLAT_PARAMS).is_some() { - let dst = &mut unsafe { - mem::transmute::<_, &mut [MaybeUninit; MAX_FLAT_PARAMS]>(dst) - } - .iter_mut(); - - params - .iter() - .zip(params_ty.types.iter()) - .try_for_each(|(param, ty)| param.lower(cx, *ty, dst)) - } else { - self.store_args(cx, ¶ms_ty, params, dst) + if store.0[self.0].options.async_() { + #[cfg(feature = "component-model-async")] + { + for (result, slot) in self + .call_raw_async( + store, + params.iter().cloned().collect(), + Self::lower_args_async, + Self::lift_results_async, + )? + .0 + .into_iter() + .zip(results) + { + *slot = result; } - }, - |cx, results_ty, src: &[ValRaw; MAX_FLAT_RESULTS]| { - let results_ty = match results_ty { - InterfaceType::Tuple(i) => &cx.types[i], - _ => unreachable!(), - }; - if results_ty.abi.flat_count(MAX_FLAT_RESULTS).is_some() { - let mut flat = src.iter(); - for (ty, slot) in results_ty.types.iter().zip(results) { - *slot = Val::lift(cx, *ty, &mut flat)?; + Ok(()) + } + #[cfg(not(feature = "component-model-async"))] + { + unreachable!( + "async-lifted exports should have failed validation \ + when `component-model-async` feature disabled" + ); + } + } else { + self.call_raw( + store, + ¶ms.iter().cloned().collect::>(), + Self::lower_args_sync, + |cx, results_ty, src: &[ValRaw; MAX_FLAT_RESULTS]| { + for (result, slot) in Self::lift_results_sync(cx, results_ty, src)? + .into_iter() + .zip(results) + { + *slot = result; } Ok(()) - } else { - Self::load_results(cx, results_ty, results, &mut src.iter()) - } + }, + ) + } + } + + #[cfg(feature = "component-model-async")] + fn call_raw_async<'a, T: Send, Params, Return: Send + Sync + 'static, LowerParams>( + &self, + store: StoreContextMut<'a, T>, + params: Params, + lower: LowerFn, + lift: LiftFn, + ) -> Result<(Return, StoreContextMut<'a, T>)> + where + LowerParams: Copy, + { + let me = self.0; + // Note that we smuggle the params through as raw pointers to avoid + // requiring `Params: Send + Sync + 'static` bounds on this function, + // which would prevent passing references as parameters. Technically, + // we don't need to do that for the return type, but we do it anyway for + // symmetry. + // + // This is only safe because `concurrent::call` will either consume or + // drop the contexts before returning. + concurrent::call::<_, LowerParams, _>( + store, + lower_params_with_context::>, + concurrent::LiftLowerContext { + pointer: Box::into_raw(Box::new((me, params, lower))) as _, + dropper: drop_context::<(Stored, Params, LowerFn)>, }, + lift_results_with_context::>, + concurrent::LiftLowerContext { + pointer: Box::into_raw(Box::new((me, lift))) as _, + dropper: drop_context::<(Stored, LiftFn)>, + }, + *self, + ) + } + + #[cfg(feature = "component-model-async")] + fn start_call_raw_async< + 'a, + T: Send, + Params: Send + Sync + 'static, + Return: Send + Sync + 'static, + LowerParams, + >( + &self, + store: StoreContextMut<'a, T>, + params: Params, + lower: LowerFn, + lift: LiftFn, + ) -> Result<(Promise, StoreContextMut<'a, T>)> + where + LowerParams: Copy, + { + let me = self.0; + concurrent::start_call::<_, LowerParams, _>( + store, + lower_params_with_context::>, + concurrent::LiftLowerContext { + pointer: Box::into_raw(Box::new((me, params, lower))) as _, + dropper: drop_context::<(Stored, Params, LowerFn)>, + }, + lift_results_with_context::>, + concurrent::LiftLowerContext { + pointer: Box::into_raw(Box::new((me, lift))) as _, + dropper: drop_context::<(Stored, LiftFn)>, + }, + *self, ) } @@ -389,7 +555,7 @@ impl Func { /// happening. fn call_raw( &self, - store: &mut StoreContextMut<'_, T>, + mut store: StoreContextMut<'_, T>, params: &Params, lower: impl FnOnce( &mut LowerContext<'_, T>, @@ -468,7 +634,7 @@ impl Func { // on the correctness of this module and `ComponentType` // implementations, hence `ComponentType` being an `unsafe` trait. crate::Func::call_unchecked_raw( - store, + &mut store, export.func_ref, core::ptr::slice_from_raw_parts_mut( space.as_mut_ptr().cast(), @@ -642,8 +808,56 @@ impl Func { Ok(()) } + fn lower_args_sync( + cx: &mut LowerContext<'_, T>, + params: &Vec, + params_ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>, + ) -> Result<()> { + Self::lower_args(cx, params, params_ty, dst, false) + } + + #[cfg(feature = "component-model-async")] + fn lower_args_async( + cx: &mut LowerContext<'_, T>, + params: &Vec, + params_ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>, + ) -> Result<()> { + Self::lower_args(cx, params, params_ty, dst, true) + } + + fn lower_args( + cx: &mut LowerContext<'_, T>, + params: &[Val], + params_ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>, + async_: bool, + ) -> Result<()> { + let params_ty = match params_ty { + InterfaceType::Tuple(i) => &cx.types[i], + _ => unreachable!(), + }; + if params_ty.abi.flat_count(MAX_FLAT_PARAMS).is_some() { + let dst = &mut unsafe { + mem::transmute::<_, &mut [MaybeUninit; MAX_FLAT_PARAMS]>(dst) + } + .iter_mut(); + + params + .iter() + .zip(params_ty.types.iter()) + .try_for_each(|(param, ty)| param.lower(cx, *ty, dst)) + } else { + if async_ { + todo!() + } else { + Self::store_args(cx, ¶ms_ty, params, dst) + } + } + } + fn store_args( - &self, cx: &mut LowerContext<'_, T>, params_ty: &TypeTuple, args: &[Val], @@ -662,12 +876,59 @@ impl Func { Ok(()) } + fn lift_results_sync( + cx: &mut LiftContext<'_>, + results_ty: InterfaceType, + src: &[ValRaw], + ) -> Result> { + Self::lift_results(cx, results_ty, src, false) + } + + #[cfg(feature = "component-model-async")] + fn lift_results_async( + cx: &mut LiftContext<'_>, + results_ty: InterfaceType, + src: &[ValRaw], + ) -> Result> { + Self::lift_results(cx, results_ty, src, true) + } + + fn lift_results( + cx: &mut LiftContext<'_>, + results_ty: InterfaceType, + src: &[ValRaw], + async_: bool, + ) -> Result> { + let results_ty = match results_ty { + InterfaceType::Tuple(i) => &cx.types[i], + _ => unreachable!(), + }; + let limit = if async_ { + MAX_FLAT_PARAMS + } else { + MAX_FLAT_RESULTS + }; + if results_ty.abi.flat_count(limit).is_some() { + let mut flat = src.iter(); + results_ty + .types + .iter() + .map(|ty| Val::lift(cx, *ty, &mut flat)) + .collect() + } else { + if async_ { + todo!() + } else { + Self::load_results(cx, results_ty, &mut src.iter()) + } + } + } + fn load_results( cx: &mut LiftContext<'_>, results_ty: &TypeTuple, - results: &mut [Val], src: &mut core::slice::Iter<'_, ValRaw>, - ) -> Result<()> { + ) -> Result> { // FIXME: needs to read an i64 for memory64 let ptr = usize::try_from(src.next().unwrap().get_u32())?; if ptr % usize::try_from(results_ty.abi.align32)? != 0 { @@ -681,11 +942,156 @@ impl Func { .ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?; let mut offset = 0; - for (ty, slot) in results_ty.types.iter().zip(results) { - let abi = cx.types.canonical_abi(ty); - let offset = abi.next_field32_size(&mut offset); - *slot = Val::load(cx, *ty, &bytes[offset..][..abi.size32 as usize])?; + results_ty + .types + .iter() + .map(|ty| { + let abi = cx.types.canonical_abi(ty); + let offset = abi.next_field32_size(&mut offset); + Val::load(cx, *ty, &bytes[offset..][..abi.size32 as usize]) + }) + .collect() + } +} + +#[cfg(feature = "component-model-async")] +fn drop_context(pointer: *mut u8) { + drop(unsafe { Box::from_raw(pointer as *mut T) }) +} + +#[cfg(feature = "component-model-async")] +fn lower_params_with_context< + Params, + LowerParams, + T, + F: FnOnce( + &mut LowerContext, + &Params, + InterfaceType, + &mut MaybeUninit, + ) -> Result<()> + + Send + + Sync, +>( + context: LiftLowerContext, + store: *mut dyn crate::vm::VMStore, + lowered: &mut [MaybeUninit], +) -> Result<()> { + let (me, params, lower) = unsafe { + *Box::from_raw( + std::mem::ManuallyDrop::new(context).pointer as *mut (Stored, Params, F), + ) + }; + + lower_params(store, lowered, me, params, lower) +} + +#[cfg(feature = "component-model-async")] +fn lower_params< + Params, + LowerParams, + T, + F: FnOnce( + &mut LowerContext, + &Params, + InterfaceType, + &mut MaybeUninit, + ) -> Result<()> + + Send + + Sync, +>( + store: *mut dyn crate::vm::VMStore, + lowered: &mut [MaybeUninit], + me: Stored, + params: Params, + lower: F, +) -> Result<()> { + use crate::component::storage::slice_to_storage_mut; + + let mut store = unsafe { StoreContextMut(&mut *store.cast()) }; + let FuncData { + options, + instance, + component_instance, + ty, + .. + } = store.0[me]; + + let instance = store.0[instance.0].as_ref().unwrap(); + let types = instance.component_types().clone(); + let instance_ptr = instance.instance_ptr(); + let mut flags = instance.instance().instance_flags(component_instance); + + unsafe { + if !flags.may_enter() { + bail!(crate::Trap::CannotEnterComponent); } + + flags.set_may_leave(false); + let mut cx = LowerContext::new(store.as_context_mut(), &options, &types, instance_ptr); + cx.enter_call(); + let result = lower( + &mut cx, + ¶ms, + InterfaceType::Tuple(types[ty].params), + slice_to_storage_mut(lowered), + ); + flags.set_may_leave(true); + result?; Ok(()) } } + +#[cfg(feature = "component-model-async")] +fn lift_results_with_context< + Return: Send + Sync + 'static, + T, + F: FnOnce(&mut LiftContext, InterfaceType, &[ValRaw]) -> Result + Send + Sync, +>( + context: LiftLowerContext, + store: *mut dyn crate::vm::VMStore, + lowered: &[ValRaw], +) -> Result>> { + let (me, lift) = unsafe { + *Box::from_raw(std::mem::ManuallyDrop::new(context).pointer as *mut (Stored, F)) + }; + + lift_results::<_, T, _>(store, lowered, me, lift) +} + +#[cfg(feature = "component-model-async")] +fn lift_results< + Return: Send + Sync + 'static, + T, + F: FnOnce(&mut LiftContext, InterfaceType, &[ValRaw]) -> Result + Send + Sync, +>( + store: *mut dyn crate::vm::VMStore, + lowered: &[ValRaw], + me: Stored, + lift: F, +) -> Result>> { + let store = unsafe { StoreContextMut::(&mut *store.cast()) }; + let FuncData { + options, + instance, + component_instance, + ty, + .. + } = store.0[me]; + + let instance = store.0[instance.0].as_ref().unwrap(); + let types = instance.component_types().clone(); + let instance_ptr = instance.instance_ptr(); + let mut flags = instance.instance().instance_flags(component_instance); + + store.0[me].post_return_arg = Some(ValRaw::i32(0)); + + unsafe { + flags.set_needs_post_return(true); + Ok(Some(Box::new(lift( + &mut LiftContext::new(store.0, &options, &types, instance_ptr), + InterfaceType::Tuple(types[ty].results), + lowered, + )?) as Box)) + } +} diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index 1461554ad8a9..45e34c48d4bd 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -1,3 +1,4 @@ +use crate::component::concurrent; use crate::component::func::{LiftContext, LowerContext, Options}; use crate::component::matching::InstanceType; use crate::component::storage::slice_to_storage_mut; @@ -10,13 +11,28 @@ use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition, VMOpaqueContext}; use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw}; use alloc::sync::Arc; use core::any::Any; +use core::future::Future; +use core::iter; use core::mem::{self, MaybeUninit}; use core::ptr::NonNull; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, StringEncoding, TypeFuncIndex, - MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + CanonicalAbiInfo, ComponentTypes, InterfaceType, RuntimeComponentInstanceIndex, StringEncoding, + TypeFuncIndex, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, }; +#[cfg(feature = "component-model-async")] +use crate::runtime::vm::SendSyncPtr; + +#[cfg(feature = "component-model-async")] +const STATUS_PARAMS_READ: u32 = 1; +#[cfg(feature = "component-model-async")] +const STATUS_DONE: u32 = 3; + +struct Ptr(*const F); + +unsafe impl Sync for Ptr {} +unsafe impl Send for Ptr {} + pub struct HostFunc { entrypoint: VMLoweringCallee, typecheck: Box) -> Result<()>) + Send + Sync>, @@ -28,9 +44,23 @@ impl HostFunc { where F: Fn(StoreContextMut, P) -> Result + Send + Sync + 'static, P: ComponentNamedList + Lift + 'static, - R: ComponentNamedList + Lower + 'static, + R: ComponentNamedList + Lower + Send + Sync + 'static, + { + Self::from_concurrent(move |store, params| { + let result = func(store, params); + async move { concurrent::for_any(move |_| result) } + }) + } + + pub(crate) fn from_concurrent(func: F) -> Arc + where + N: FnOnce(StoreContextMut) -> Result + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, P) -> FN + Send + Sync + 'static, + P: ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + Send + Sync + 'static, { - let entrypoint = Self::entrypoint::; + let entrypoint = Self::entrypoint::; Arc::new(HostFunc { entrypoint, typecheck: Box::new(typecheck::), @@ -38,36 +68,42 @@ impl HostFunc { }) } - extern "C" fn entrypoint( + extern "C" fn entrypoint( cx: *mut VMOpaqueContext, data: *mut u8, ty: u32, + caller_instance: u32, flags: *mut u8, memory: *mut VMMemoryDefinition, realloc: *mut VMFuncRef, string_encoding: u8, + async_: u8, storage: *mut MaybeUninit, storage_len: usize, ) -> bool where - F: Fn(StoreContextMut, P) -> Result, + N: FnOnce(StoreContextMut) -> Result + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, P) -> FN + Send + Sync + 'static, P: ComponentNamedList + Lift + 'static, - R: ComponentNamedList + Lower + 'static, + R: ComponentNamedList + Lower + Send + Sync + 'static, { - let data = data as *const F; + let data = Ptr(data as *const F); unsafe { call_host_and_handle_result::(cx, |instance, types, store| { - call_host::<_, _, _, _>( + call_host( instance, types, store, TypeFuncIndex::from_u32(ty), + RuntimeComponentInstanceIndex::from_u32(caller_instance), InstanceFlags::from_raw(flags), memory, realloc, StringEncoding::from_u8(string_encoding).unwrap(), + async_ != 0, core::slice::from_raw_parts_mut(storage, storage_len), - |store, args| (*data)(store, args), + move |store, args| (*data.0)(store, args), ) }) } @@ -76,14 +112,30 @@ impl HostFunc { pub(crate) fn new_dynamic(func: F) -> Arc where F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + { + Self::new_dynamic_concurrent(move |store, params: Vec, result_count| { + let mut results = iter::repeat(Val::Bool(false)) + .take(result_count) + .collect::>(); + let result = func(store, ¶ms, &mut results); + let result = result.map(move |()| results); + async move { concurrent::for_any(move |_| result) } + }) + } + + pub(crate) fn new_dynamic_concurrent(f: F) -> Arc + where + N: FnOnce(StoreContextMut) -> Result> + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Vec, usize) -> FN + Send + Sync + 'static, { Arc::new(HostFunc { - entrypoint: dynamic_entrypoint::, + entrypoint: dynamic_entrypoint::, // This function performs dynamic type checks and subsequently does // not need to perform up-front type checks. Instead everything is // dynamically managed at runtime. typecheck: Box::new(move |_expected_index, _expected_types| Ok(())), - func: Box::new(func), + func: Box::new(f), }) } @@ -133,22 +185,26 @@ where /// This function is in general `unsafe` as the validity of all the parameters /// must be upheld. Generally that's done by ensuring this is only called from /// the select few places it's intended to be called from. -unsafe fn call_host( +unsafe fn call_host( instance: *mut ComponentInstance, types: &Arc, mut cx: StoreContextMut<'_, T>, ty: TypeFuncIndex, + caller_instance: RuntimeComponentInstanceIndex, mut flags: InstanceFlags, memory: *mut VMMemoryDefinition, realloc: *mut VMFuncRef, string_encoding: StringEncoding, + async_: bool, storage: &mut [MaybeUninit], closure: F, ) -> Result<()> where + N: FnOnce(StoreContextMut) -> Result + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Params) -> FN + 'static, Params: Lift, - Return: Lower, - F: FnOnce(StoreContextMut<'_, T>, Params) -> Result, + Return: Lower + Send + Sync + 'static, { /// Representation of arguments to this function when a return pointer is in /// use, namely the argument list is followed by a single value which is the @@ -173,6 +229,8 @@ where NonNull::new(memory), NonNull::new(realloc), string_encoding, + async_, + None, ); // Perform a dynamic check that this instance can indeed be left. Exiting @@ -186,39 +244,85 @@ where let param_tys = InterfaceType::Tuple(ty.params); let result_tys = InterfaceType::Tuple(ty.results); - // There's a 2x2 matrix of whether parameters and results are stored on the - // stack or on the heap. Each of the 4 branches here have a different - // representation of the storage of arguments/returns. - // - // Also note that while four branches are listed here only one is taken for - // any particular `Params` and `Return` combination. This should be - // trivially DCE'd by LLVM. Perhaps one day with enough const programming in - // Rust we can make monomorphizations of this function codegen only one - // branch, but today is not that day. - let mut storage: Storage<'_, Params, Return> = if Params::flatten_count() <= MAX_FLAT_PARAMS { - if Return::flatten_count() <= MAX_FLAT_RESULTS { - Storage::Direct(slice_to_storage_mut(storage)) - } else { - Storage::ResultsIndirect(slice_to_storage_mut(storage).assume_init_ref()) + if async_ { + #[cfg(feature = "component-model-async")] + { + let paramptr = storage[0].assume_init(); + let retptr = storage[1].assume_init(); + + let params = { + let lift = &mut LiftContext::new(cx.0, &options, types, instance); + lift.enter_call(); + let ptr = validate_inbounds::(lift.memory(), ¶mptr)?; + Params::load(lift, param_tys, &lift.memory()[ptr..][..Params::SIZE32])? + }; + + let future = closure(cx.as_context_mut(), params); + + let task = + concurrent::first_poll(instance, cx.as_context_mut(), future, caller_instance, { + let types = types.clone(); + let instance = SendSyncPtr::new(NonNull::new(instance).unwrap()); + move |cx, ret: Return| { + let mut lower = LowerContext::new(cx, &options, &types, instance.as_ptr()); + let ptr = validate_inbounds::(lower.as_slice_mut(), &retptr)?; + ret.store(&mut lower, result_tys, ptr) + } + })?; + + let status = if let Some(task) = task { + (STATUS_PARAMS_READ << 30) | task + } else { + STATUS_DONE << 30 + }; + + storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); + } + #[cfg(not(feature = "component-model-async"))] + { + unreachable!( + "async-lowered imports should have failed validation \ + when `component-model-async` feature disabled" + ); } } else { - if Return::flatten_count() <= MAX_FLAT_RESULTS { - Storage::ParamsIndirect(slice_to_storage_mut(storage)) + // There's a 2x2 matrix of whether parameters and results are stored on the + // stack or on the heap. Each of the 4 branches here have a different + // representation of the storage of arguments/returns. + // + // Also note that while four branches are listed here only one is taken for + // any particular `Params` and `Return` combination. This should be + // trivially DCE'd by LLVM. Perhaps one day with enough const programming in + // Rust we can make monomorphizations of this function codegen only one + // branch, but today is not that day. + let mut storage: Storage<'_, Params, Return> = if Params::flatten_count() <= MAX_FLAT_PARAMS + { + if Return::flatten_count() <= MAX_FLAT_RESULTS { + Storage::Direct(slice_to_storage_mut(storage)) + } else { + Storage::ResultsIndirect(slice_to_storage_mut(storage).assume_init_ref()) + } } else { - Storage::Indirect(slice_to_storage_mut(storage).assume_init_ref()) - } - }; - let mut lift = LiftContext::new(cx.0, &options, types, instance); - lift.enter_call(); - let params = storage.lift_params(&mut lift, param_tys)?; + if Return::flatten_count() <= MAX_FLAT_RESULTS { + Storage::ParamsIndirect(slice_to_storage_mut(storage)) + } else { + Storage::Indirect(slice_to_storage_mut(storage).assume_init_ref()) + } + }; + let mut lift = LiftContext::new(cx.0, &options, types, instance); + lift.enter_call(); + let params = storage.lift_params(&mut lift, param_tys)?; - let ret = closure(cx.as_context_mut(), params)?; - flags.set_may_leave(false); - let mut lower = LowerContext::new(cx, &options, types, instance); - storage.lower_results(&mut lower, result_tys, ret)?; - flags.set_may_leave(true); + let future = closure(cx.as_context_mut(), params); - lower.exit_call()?; + let (ret, cx) = concurrent::poll_and_block(cx, future, caller_instance)?; + + flags.set_may_leave(false); + let mut lower = LowerContext::new(cx, &options, types, instance); + storage.lower_results(&mut lower, result_tys, ret)?; + flags.set_may_leave(true); + lower.exit_call()?; + } return Ok(()); @@ -273,7 +377,7 @@ where } } -fn validate_inbounds(memory: &[u8], ptr: &ValRaw) -> Result { +pub(crate) fn validate_inbounds(memory: &[u8], ptr: &ValRaw) -> Result { // FIXME: needs memory64 support let ptr = usize::try_from(ptr.get_u32())?; if ptr % usize::try_from(T::ALIGN32)? != 0 { @@ -311,26 +415,32 @@ unsafe fn call_host_and_handle_result( }) } -unsafe fn call_host_dynamic( +unsafe fn call_host_dynamic( instance: *mut ComponentInstance, types: &Arc, mut store: StoreContextMut<'_, T>, ty: TypeFuncIndex, + caller_instance: RuntimeComponentInstanceIndex, mut flags: InstanceFlags, memory: *mut VMMemoryDefinition, realloc: *mut VMFuncRef, string_encoding: StringEncoding, + async_: bool, storage: &mut [MaybeUninit], closure: F, ) -> Result<()> where - F: FnOnce(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()>, + N: FnOnce(StoreContextMut) -> Result> + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Vec, usize) -> FN + 'static, { let options = Options::new( store.0.id(), NonNull::new(memory), NonNull::new(realloc), string_encoding, + async_, + None, ); // Perform a dynamic check that this instance can indeed be left. Exiting @@ -346,61 +456,136 @@ where let func_ty = &types[ty]; let param_tys = &types[func_ty.params]; let result_tys = &types[func_ty.results]; - let mut cx = LiftContext::new(store.0, &options, types, instance); - cx.enter_call(); - if let Some(param_count) = param_tys.abi.flat_count(MAX_FLAT_PARAMS) { - // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable - let mut iter = - mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..param_count]).iter(); - args = param_tys - .types - .iter() - .map(|ty| Val::lift(&mut cx, *ty, &mut iter)) - .collect::>>()?; - ret_index = param_count; - assert!(iter.next().is_none()); - } else { - let mut offset = - validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), storage[0].assume_init_ref())?; - args = param_tys - .types - .iter() - .map(|ty| { - let abi = types.canonical_abi(ty); - let size = usize::try_from(abi.size32).unwrap(); - let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; - Val::load(&mut cx, *ty, memory) - }) - .collect::>>()?; - ret_index = 1; - }; - let mut result_vals = Vec::with_capacity(result_tys.types.len()); - for _ in result_tys.types.iter() { - result_vals.push(Val::Bool(false)); - } - closure(store.as_context_mut(), &args, &mut result_vals)?; - 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) { - let mut dst = storage[..cnt].iter_mut(); - for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { - val.lower(&mut cx, *ty, &mut dst)?; + if async_ { + #[cfg(feature = "component-model-async")] + { + let paramptr = storage[0].assume_init(); + let retptr = storage[1].assume_init(); + + let params = { + let mut lift = &mut LiftContext::new(store.0, &options, types, instance); + lift.enter_call(); + let mut offset = + validate_inbounds_dynamic(¶m_tys.abi, lift.memory(), ¶mptr)?; + param_tys + .types + .iter() + .map(|ty| { + let abi = types.canonical_abi(ty); + let size = usize::try_from(abi.size32).unwrap(); + let memory = &lift.memory()[abi.next_field32_size(&mut offset)..][..size]; + Val::load(&mut lift, *ty, memory) + }) + .collect::>>()? + }; + + let future = closure(store.as_context_mut(), params, result_tys.types.len()); + + let task = concurrent::first_poll( + instance, + store.as_context_mut(), + future, + caller_instance, + { + let types = types.clone(); + let instance = SendSyncPtr::new(NonNull::new(instance).unwrap()); + let result_tys = func_ty.results; + move |store, result_vals: Vec| { + let result_tys = &types[result_tys]; + if result_vals.len() != result_tys.types.len() { + bail!("result length mismatch"); + } + + let mut lower = + LowerContext::new(store, &options, &types, instance.as_ptr()); + let mut ptr = validate_inbounds_dynamic( + &result_tys.abi, + lower.as_slice_mut(), + &retptr, + )?; + for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { + let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); + val.store(&mut lower, *ty, offset)?; + } + Ok(()) + } + }, + )?; + + let status = if let Some(task) = task { + (STATUS_PARAMS_READ << 30) | task + } else { + STATUS_DONE << 30 + }; + + storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); + } + #[cfg(not(feature = "component-model-async"))] + { + unreachable!( + "async-lowered imports should have failed validation \ + when `component-model-async` feature disabled" + ); } - assert!(dst.next().is_none()); } else { - let ret_ptr = 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); - val.store(&mut cx, *ty, offset)?; + let mut cx = LiftContext::new(store.0, &options, types, instance); + cx.enter_call(); + if let Some(param_count) = param_tys.abi.flat_count(MAX_FLAT_PARAMS) { + // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable + let mut iter = + mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..param_count]).iter(); + args = param_tys + .types + .iter() + .map(|ty| Val::lift(&mut cx, *ty, &mut iter)) + .collect::>>()?; + ret_index = param_count; + assert!(iter.next().is_none()); + } else { + let mut offset = validate_inbounds_dynamic( + ¶m_tys.abi, + cx.memory(), + storage[0].assume_init_ref(), + )?; + args = param_tys + .types + .iter() + .map(|ty| { + let abi = types.canonical_abi(ty); + let size = usize::try_from(abi.size32).unwrap(); + let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; + Val::load(&mut cx, *ty, memory) + }) + .collect::>>()?; + ret_index = 1; + }; + + let future = closure(store.as_context_mut(), args, result_tys.types.len()); + let (result_vals, store) = concurrent::poll_and_block(store, future, caller_instance)?; + + 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) { + let mut dst = storage[..cnt].iter_mut(); + for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { + val.lower(&mut cx, *ty, &mut dst)?; + } + assert!(dst.next().is_none()); + } else { + let ret_ptr = 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); + val.store(&mut cx, *ty, offset)?; + } } - } - flags.set_may_leave(true); + flags.set_may_leave(true); - cx.exit_call()?; + cx.exit_call()?; + } return Ok(()); } @@ -421,34 +606,40 @@ fn validate_inbounds_dynamic(abi: &CanonicalAbiInfo, memory: &[u8], ptr: &ValRaw Ok(ptr) } -extern "C" fn dynamic_entrypoint( +extern "C" fn dynamic_entrypoint( cx: *mut VMOpaqueContext, data: *mut u8, ty: u32, + caller_instance: u32, flags: *mut u8, memory: *mut VMMemoryDefinition, realloc: *mut VMFuncRef, string_encoding: u8, + async_: u8, storage: *mut MaybeUninit, storage_len: usize, ) -> bool where - F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + N: FnOnce(StoreContextMut) -> Result> + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Vec, usize) -> FN + Send + Sync + 'static, { - let data = data as *const F; + let data = Ptr(data as *const F); unsafe { call_host_and_handle_result(cx, |instance, types, store| { - call_host_dynamic::( + call_host_dynamic( instance, types, store, TypeFuncIndex::from_u32(ty), + RuntimeComponentInstanceIndex::from_u32(caller_instance), InstanceFlags::from_raw(flags), memory, realloc, StringEncoding::from_u8(string_encoding).unwrap(), + async_ != 0, core::slice::from_raw_parts_mut(storage, storage_len), - |store, params, results| (*data)(store, params, results), + move |store, params, results| (*data.0)(store, params, results), ) }) } diff --git a/crates/wasmtime/src/runtime/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs index cd0482965e21..0458735b4368 100644 --- a/crates/wasmtime/src/runtime/component/func/options.rs +++ b/crates/wasmtime/src/runtime/component/func/options.rs @@ -43,6 +43,11 @@ pub struct Options { /// /// This defaults to utf-8 but can be changed if necessary. string_encoding: StringEncoding, + + async_: bool, + + #[cfg_attr(not(feature = "component-model-async"), allow(unused))] + pub(crate) callback: Option>, } // The `Options` structure stores raw pointers but they're never used unless a @@ -66,12 +71,16 @@ impl Options { memory: Option>, realloc: Option>, string_encoding: StringEncoding, + async_: bool, + callback: Option>, ) -> Options { Options { store_id, memory, realloc, string_encoding, + async_, + callback, } } @@ -163,6 +172,11 @@ impl Options { pub fn store_id(&self) -> StoreId { self.store_id } + + /// Returns whether this lifting or lowering uses the async ABI. + pub fn async_(&self) -> bool { + self.async_ + } } /// A helper structure which is a "package" of the context used during lowering @@ -196,7 +210,7 @@ pub struct LowerContext<'a, T> { /// into. /// /// This pointer is required to be owned by the `store` provided. - instance: *mut ComponentInstance, + pub(crate) instance: *mut ComponentInstance, } #[doc(hidden)] @@ -402,7 +416,7 @@ pub struct LiftContext<'a> { memory: Option<&'a [u8]>, - instance: *mut ComponentInstance, + pub(crate) instance: *mut ComponentInstance, host_table: &'a mut ResourceTable, host_resource_data: &'a mut HostResourceData, diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index 95fb52186c2a..eeaf4436a75c 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -17,6 +17,9 @@ use wasmtime_environ::component::{ MAX_FLAT_RESULTS, }; +#[cfg(feature = "component-model-async")] +use crate::component::concurrent::{self, Promise}; + /// A statically-typed version of [`Func`] which takes `Params` as input and /// returns `Return`. /// @@ -154,7 +157,14 @@ where /// Panics if this is called on a function in an asynchronous store. This /// only works with functions defined within a synchronous store. Also /// panics if `store` does not own this function. - pub fn call(&self, store: impl AsContextMut, params: Params) -> Result { + pub fn call( + &self, + store: impl AsContextMut, + params: Params, + ) -> Result + where + Return: Send + Sync + 'static, + { assert!( !store.as_context().async_support(), "must use `call_async` when async support is enabled on the config" @@ -170,28 +180,215 @@ where /// only works with functions defined within an asynchronous store. Also /// panics if `store` does not own this function. #[cfg(feature = "async")] - pub async fn call_async( - &self, + pub async fn call_async( + self, mut store: impl AsContextMut, params: Params, ) -> Result where - T: Send, Params: Send + Sync, - Return: Send + Sync, + Return: Send + Sync + 'static, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "cannot use `call_async` when async support is not enabled on the config" ); - store - .on_fiber(|store| self.call_impl(store, params)) - .await? + #[cfg(feature = "component-model-async")] + { + let instance = store.0[self.func.0].component_instance; + // TODO: do we need to return the store here due to the possible + // invalidation of the reference we were passed? + concurrent::on_fiber(store, instance, move |store| self.call_impl(store, params)) + .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store + .on_fiber(|store| self.call_impl(store, params)) + .await? + } + } + + /// Start concurrent call to this function. + /// + /// Unlike [`Self::call`] and [`Self::call_async`] (both of which require + /// exclusive access to the store until the completion of the call), calls + /// made using this method may run concurrently with other calls to the same + /// instance. + #[cfg(feature = "component-model-async")] + pub async fn call_concurrent( + self, + mut store: impl AsContextMut, + params: Params, + ) -> Result> + where + Params: Send + Sync + 'static, + Return: Send + Sync + 'static, + { + let store = store.as_context_mut(); + assert!( + store.0.async_support(), + "cannot use `call_concurrent` when async support is not enabled on the config" + ); + let instance = store.0[self.func.0].component_instance; + // TODO: do we need to return the store here due to the possible + // invalidation of the reference we were passed? + concurrent::on_fiber(store, instance, move |store| { + self.start_call(store.as_context_mut(), params) + }) + .await? + .0 } - fn call_impl(&self, mut store: impl AsContextMut, params: Params) -> Result { - let store = &mut store.as_context_mut(); + #[cfg(feature = "component-model-async")] + fn start_call<'a, T: Send>( + self, + store: StoreContextMut<'a, T>, + params: Params, + ) -> Result> + where + Params: Send + Sync + 'static, + Return: Send + Sync + 'static, + { + Ok(if store.0[self.func.0].options.async_() { + #[cfg(feature = "component-model-async")] + { + if Params::flatten_count() <= MAX_FLAT_PARAMS { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + self.func.start_call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_stack_result_raw, + ) + } else { + self.func.start_call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_heap_result_guest, + ) + } + } else { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + self.func.start_call_raw_async( + store, + params, + Self::lower_heap_args_guest, + Self::lift_stack_result_raw, + ) + } else { + self.func.start_call_raw_async( + store, + params, + Self::lower_heap_args_guest, + Self::lift_heap_result_guest, + ) + } + } + } + #[cfg(not(feature = "component-model-async"))] + { + unreachable!( + "async-lifted exports should have failed validation \ + when `component-model-async` feature disabled" + ); + } + } else if Params::flatten_count() <= MAX_FLAT_PARAMS { + if Return::flatten_count() <= MAX_FLAT_RESULTS { + self.func.start_call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_stack_result_raw, + ) + } else { + self.func.start_call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_heap_result_raw, + ) + } + } else { + if Return::flatten_count() <= MAX_FLAT_RESULTS { + self.func.start_call_raw_async( + store, + params, + Self::lower_heap_args, + Self::lift_stack_result_raw, + ) + } else { + self.func.start_call_raw_async( + store, + params, + Self::lower_heap_args, + Self::lift_heap_result_raw, + ) + } + }? + .0) + } + + fn call_impl( + &self, + mut store: impl AsContextMut, + params: Params, + ) -> Result + where + Return: Send + Sync + 'static, + { + let store = store.as_context_mut(); + + if store.0[self.func.0].options.async_() { + #[cfg(feature = "component-model-async")] + { + return Ok(if Params::flatten_count() <= MAX_FLAT_PARAMS { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + self.func.call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_stack_result_raw, + ) + } else { + self.func.call_raw_async( + store, + params, + Self::lower_stack_args, + Self::lift_heap_result_guest, + ) + } + } else { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + self.func.call_raw_async( + store, + params, + Self::lower_heap_args_guest, + Self::lift_stack_result_raw, + ) + } else { + self.func.call_raw_async( + store, + params, + Self::lower_heap_args_guest, + Self::lift_heap_result_guest, + ) + } + }? + .0); + } + #[cfg(not(feature = "component-model-async"))] + { + bail!( + "must enable the `component-model-async` feature to call async-lifted exports" + ) + } + } + // Note that this is in theory simpler than it might read at this time. // Here we're doing a runtime dispatch on the `flatten_count` for the // params/results to see whether they're inbounds. This creates 4 cases @@ -266,8 +463,6 @@ where ty: InterfaceType, dst: &mut MaybeUninit, ) -> Result<()> { - assert!(Params::flatten_count() > MAX_FLAT_PARAMS); - // Memory must exist via validation if the arguments are stored on the // heap, so we can create a `MemoryMut` at this point. Afterwards // `realloc` is used to allocate space for all the arguments and then @@ -302,10 +497,20 @@ where ty: InterfaceType, dst: &Return::Lower, ) -> Result { - assert!(Return::flatten_count() <= MAX_FLAT_RESULTS); Return::lift(cx, ty, dst) } + #[cfg(feature = "component-model-async")] + fn lift_stack_result_raw( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + dst: &[ValRaw], + ) -> Result { + Self::lift_stack_result(cx, ty, unsafe { + crate::component::storage::slice_to_storage(dst) + }) + } + /// Lift the result of a function where the result is stored indirectly on /// the heap. fn lift_heap_result( @@ -328,6 +533,36 @@ where Return::load(cx, ty, bytes) } + #[cfg(feature = "component-model-async")] + fn lift_heap_result_raw( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + dst: &[ValRaw], + ) -> Result { + Self::lift_heap_result(cx, ty, &dst[0]) + } + + #[cfg(feature = "component-model-async")] + fn lower_heap_args_guest( + cx: &mut LowerContext<'_, T>, + params: &Params, + ty: InterfaceType, + dst: &mut MaybeUninit, + ) -> Result<()> { + _ = (cx, params, ty, dst); + todo!() + } + + #[cfg(feature = "component-model-async")] + fn lift_heap_result_guest( + cx: &mut LiftContext<'_>, + ty: InterfaceType, + dst: &[ValRaw], + ) -> Result { + _ = (cx, ty, dst); + todo!() + } + /// See [`Func::post_return`] pub fn post_return(&self, store: impl AsContextMut) -> Result<()> { self.func.post_return(store) @@ -1504,7 +1739,7 @@ pub struct WasmList { } impl WasmList { - fn new( + pub(crate) fn new( ptr: usize, len: usize, cx: &mut LiftContext<'_>, @@ -2456,6 +2691,9 @@ pub fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::Enum(_) => "enum", InterfaceType::Own(_) => "owned resource", InterfaceType::Borrow(_) => "borrowed resource", + InterfaceType::Future(_) => "future", + InterfaceType::Stream(_) => "stream", + InterfaceType::ErrorContext(_) => "error-context", } } diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index bb7c337eb842..90a4b068d52b 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -1,3 +1,4 @@ +use crate::component::concurrent; use crate::component::func::HostFunc; use crate::component::matching::InstanceType; use crate::component::{ @@ -48,7 +49,7 @@ pub(crate) struct InstanceData { // of the component can be thrown away (theoretically). component: Component, - state: OwnedComponentInstance, + pub(crate) state: OwnedComponentInstance, /// Arguments that this instance used to be instantiated. /// @@ -512,9 +513,39 @@ impl<'a> Instantiator<'a> { } } - fn run(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> { + fn run(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> { let env_component = self.component.env_component(); + self.data.state.set_async_callbacks( + concurrent::task_backpressure::, + concurrent::task_return::, + concurrent::task_wait::, + concurrent::task_poll::, + concurrent::task_yield::, + concurrent::subtask_drop::, + concurrent::async_enter::, + concurrent::async_exit::, + concurrent::future_new::, + concurrent::future_write::, + concurrent::future_read::, + concurrent::future_cancel_write::, + concurrent::future_cancel_read::, + concurrent::future_close_writable::, + concurrent::future_close_readable::, + concurrent::stream_new::, + concurrent::stream_write::, + concurrent::stream_read::, + concurrent::stream_cancel_write::, + concurrent::stream_cancel_read::, + concurrent::stream_close_writable::, + concurrent::stream_close_readable::, + concurrent::flat_stream_write::, + concurrent::flat_stream_read::, + concurrent::error_context_new::, + concurrent::error_context_debug_message::, + concurrent::error_context_drop::, + ); + // Before all initializers are processed configure all destructors for // host-defined resources. No initializer will correspond to these and // it's required to happen before they're needed, so execute this first. @@ -607,6 +638,10 @@ impl<'a> Instantiator<'a> { self.extract_realloc(store.0, realloc) } + GlobalInitializer::ExtractCallback(callback) => { + self.extract_callback(store.0, callback) + } + GlobalInitializer::ExtractPostReturn(post_return) => { self.extract_post_return(store.0, post_return) } @@ -654,6 +689,16 @@ impl<'a> Instantiator<'a> { self.data.state.set_runtime_realloc(realloc.index, func_ref); } + fn extract_callback(&mut self, store: &mut StoreOpaque, callback: &ExtractCallback) { + let func_ref = match self.data.lookup_def(store, &callback.def) { + crate::runtime::vm::Export::Function(f) => f.func_ref, + _ => unreachable!(), + }; + self.data + .state + .set_runtime_callback(callback.index, func_ref); + } + fn extract_post_return(&mut self, store: &mut StoreOpaque, post_return: &ExtractPostReturn) { let func_ref = match self.data.lookup_def(store, &post_return.def) { crate::runtime::vm::Export::Function(f) => f.func_ref, @@ -796,7 +841,10 @@ impl InstancePre { /// Performs the instantiation process into the store specified. // // TODO: needs more docs - pub fn instantiate(&self, store: impl AsContextMut) -> Result { + pub fn instantiate(&self, store: impl AsContextMut) -> Result + where + T: 'static, + { assert!( !store.as_context().async_support(), "must use async instantiation when async support is enabled" @@ -814,7 +862,7 @@ impl InstancePre { mut store: impl AsContextMut, ) -> Result where - T: Send, + T: Send + 'static, { let mut store = store.as_context_mut(); assert!( @@ -824,7 +872,10 @@ impl InstancePre { store.on_fiber(|store| self.instantiate_impl(store)).await? } - fn instantiate_impl(&self, mut store: impl AsContextMut) -> Result { + fn instantiate_impl(&self, mut store: impl AsContextMut) -> Result + where + T: 'static, + { let mut store = store.as_context_mut(); store .engine() diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs index 31a3d5254884..31457ac781fd 100644 --- a/crates/wasmtime/src/runtime/component/linker.rs +++ b/crates/wasmtime/src/runtime/component/linker.rs @@ -265,7 +265,10 @@ impl Linker { &self, store: impl AsContextMut, component: &Component, - ) -> Result { + ) -> Result + where + T: 'static, + { assert!( !store.as_context().async_support(), "must use async instantiation when async support is enabled" @@ -290,7 +293,7 @@ impl Linker { component: &Component, ) -> Result where - T: Send, + T: Send + 'static, { assert!( store.as_context().async_support(), @@ -382,7 +385,7 @@ impl LinkerInstance<'_, T> { } } - /// Defines a new host-provided function into this [`Linker`]. + /// Defines a new host-provided function into this [`LinkerInstance`]. /// /// This method is used to give host functions to wasm components. The /// `func` provided will be callable from linked components with the type @@ -404,13 +407,13 @@ impl LinkerInstance<'_, T> { where F: Fn(StoreContextMut, Params) -> Result + Send + Sync + 'static, Params: ComponentNamedList + Lift + 'static, - Return: ComponentNamedList + Lower + 'static, + Return: ComponentNamedList + Lower + Send + Sync + 'static, { self.insert(name, Definition::Func(HostFunc::from_closure(func)))?; Ok(()) } - /// Defines a new host-provided async function into this [`Linker`]. + /// Defines a new host-provided async function into this [`LinkerInstance`]. /// /// This is exactly like [`Self::func_wrap`] except it takes an async /// host function. @@ -425,20 +428,65 @@ impl LinkerInstance<'_, T> { + Sync + 'static, Params: ComponentNamedList + Lift + 'static, - Return: ComponentNamedList + Lower + 'static, + Return: ComponentNamedList + Lower + Send + Sync + 'static, { assert!( self.engine.config().async_support, "cannot use `func_wrap_async` without enabling async support in the config" ); + let ff = move |mut store: StoreContextMut<'_, T>, params: Params| -> Result { - let async_cx = store.as_context_mut().0.async_cx().expect("async cx"); - let mut future = Pin::from(f(store.as_context_mut(), params)); - unsafe { async_cx.block_on(future.as_mut()) }? + #[cfg(feature = "component-model-async")] + { + let async_cx = crate::component::concurrent::AsyncCx::new(&mut store); + let mut future = Pin::from(f(store.as_context_mut(), params)); + unsafe { async_cx.block_on::(future.as_mut(), None) }?.0 + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = store.as_context_mut().0.async_cx().expect("async cx"); + let mut future = Pin::from(f(store.as_context_mut(), params)); + unsafe { async_cx.block_on(future.as_mut()) }? + } }; self.func_wrap(name, ff) } + /// Defines a new host-provided async function into this [`LinkerInstance`]. + /// + /// This allows the caller to register host functions with the + /// LinkerInstance such that multiple calls to such functions can run + /// concurrently. This isn't possible with the existing func_wrap_async + /// method because it takes a function which returns a future that owns a + /// unique reference to the Store, meaning the Store can't be used for + /// anything else until the future resolves. + /// + /// Ideally, we'd have a way to thread a `StoreContextMut` through an + /// arbitrary `Future` such that it has access to the `Store` only while + /// being polled (i.e. between, but not across, await points). However, + /// there's currently no way to express that in async Rust, so we make do + /// with a more awkward scheme: each function registered using + /// `func_wrap_concurrent` gets access to the `Store` twice: once before + /// doing any concurrent operations (i.e. before awaiting) and once + /// afterward. This allows multiple calls to proceed concurrently without + /// any one of them monopolizing the store. + #[cfg(feature = "component-model-async")] + pub fn func_wrap_concurrent(&mut self, name: &str, f: F) -> Result<()> + where + N: FnOnce(StoreContextMut) -> Result + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Params) -> FN + Send + Sync + 'static, + Params: ComponentNamedList + Lift + 'static, + Return: ComponentNamedList + Lower + Send + Sync + 'static, + { + assert!( + self.engine.config().async_support, + "cannot use `func_wrap_concurrent` without enabling async support in the config" + ); + self.insert(name, Definition::Func(HostFunc::from_concurrent(f)))?; + Ok(()) + } + /// Define a new host-provided function using dynamically typed values. /// /// The `name` provided is the name of the function to define and the @@ -568,13 +616,60 @@ impl LinkerInstance<'_, T> { "cannot use `func_new_async` without enabling async support in the config" ); let ff = move |mut store: StoreContextMut<'_, T>, params: &[Val], results: &mut [Val]| { - let async_cx = store.as_context_mut().0.async_cx().expect("async cx"); - let mut future = Pin::from(f(store.as_context_mut(), params, results)); - unsafe { async_cx.block_on(future.as_mut()) }? + #[cfg(feature = "component-model-async")] + { + let async_cx = crate::component::concurrent::AsyncCx::new(&mut store); + let mut future = Pin::from(f(store.as_context_mut(), params, results)); + unsafe { async_cx.block_on::(future.as_mut(), None) }?.0 + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = store.as_context_mut().0.async_cx().expect("async cx"); + let mut future = Pin::from(f(store.as_context_mut(), params, results)); + unsafe { async_cx.block_on(future.as_mut()) }? + } }; self.func_new(name, ff) } + /// Define a new host-provided async function using dynamic types. + /// + /// This allows the caller to register host functions with the + /// `LinkerInstance` such that multiple calls to such functions can run + /// concurrently. This isn't possible with the existing func_wrap_async + /// method because it takes a function which returns a future that owns a + /// unique reference to the Store, meaning the Store can't be used for + /// anything else until the future resolves. + /// + /// Ideally, we'd have a way to thread a `StoreContextMut` through an + /// arbitrary `Future` such that it has access to the `Store` only while + /// being polled (i.e. between, but not across, await points). However, + /// there's currently no way to express that in async Rust, so we make do + /// with a more awkward scheme: each function registered using + /// `func_wrap_concurrent` gets access to the `Store` twice: once before + /// doing any concurrent operations (i.e. before awaiting) and once + /// afterward. This allows multiple calls to proceed concurrently without + /// any one of them monopolizing the store. + #[cfg(feature = "component-model-async")] + pub fn func_new_concurrent(&mut self, name: &str, f: F) -> Result<()> + where + N: FnOnce(StoreContextMut) -> Result> + Send + Sync + 'static, + FN: Future + Send + Sync + 'static, + F: Fn(StoreContextMut, Vec) -> FN + Send + Sync + 'static, + { + assert!( + self.engine.config().async_support, + "cannot use `func_wrap_concurrent` without enabling async support in the config" + ); + self.insert( + name, + Definition::Func(HostFunc::new_dynamic_concurrent(move |store, params, _| { + f(store, params) + })), + )?; + Ok(()) + } + /// Defines a [`Module`] within this instance. /// /// This can be used to provide a core wasm [`Module`] as an import to a @@ -640,11 +735,21 @@ impl LinkerInstance<'_, T> { let dtor = Arc::new(crate::func::HostFunc::wrap_inner( &self.engine, move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| { - let async_cx = cx.as_context_mut().0.async_cx().expect("async cx"); - let mut future = Pin::from(dtor(cx.as_context_mut(), param)); - match unsafe { async_cx.block_on(future.as_mut()) } { - Ok(Ok(())) => Ok(()), - Ok(Err(trap)) | Err(trap) => Err(trap), + #[cfg(feature = "component-model-async")] + { + let async_cx = + crate::component::concurrent::AsyncCx::new(&mut cx.as_context_mut()); + let mut future = Pin::from(dtor(cx.as_context_mut(), param)); + unsafe { async_cx.block_on(future.as_mut(), None::>) }?.0 + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = cx.as_context_mut().0.async_cx().expect("async cx"); + let mut future = Pin::from(dtor(cx.as_context_mut(), param)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(Ok(())) => Ok(()), + Ok(Err(trap)) | Err(trap) => Err(trap), + } } }, )); diff --git a/crates/wasmtime/src/runtime/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs index 4222daa6dc62..d6cac001cb9a 100644 --- a/crates/wasmtime/src/runtime/component/matching.rs +++ b/crates/wasmtime/src/runtime/component/matching.rs @@ -1,5 +1,6 @@ use crate::component::func::HostFunc; use crate::component::linker::{Definition, Strings}; +use crate::component::types::{FutureType, StreamType}; use crate::component::ResourceType; use crate::prelude::*; use crate::runtime::vm::component::ComponentInstance; @@ -9,7 +10,7 @@ use alloc::sync::Arc; use core::any::Any; use wasmtime_environ::component::{ ComponentTypes, NameMap, ResourceIndex, TypeComponentInstance, TypeDef, TypeFuncIndex, - TypeModule, TypeResourceTableIndex, + TypeFutureTableIndex, TypeModule, TypeResourceTableIndex, TypeStreamTableIndex, }; use wasmtime_environ::PrimaryMap; @@ -199,6 +200,14 @@ impl<'a> InstanceType<'a> { .copied() .unwrap_or_else(|| ResourceType::uninstantiated(&self.types, index)) } + + pub fn future_type(&self, index: TypeFutureTableIndex) -> FutureType { + FutureType::from(self.types[index].ty, self) + } + + pub fn stream_type(&self, index: TypeStreamTableIndex) -> StreamType { + StreamType::from(self.types[index].ty, self) + } } /// Small helper method to downcast an `Arc` borrow into a borrow of a concrete diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs index 5c347102903a..e42d786e9710 100644 --- a/crates/wasmtime/src/runtime/component/mod.rs +++ b/crates/wasmtime/src/runtime/component/mod.rs @@ -101,6 +101,8 @@ #![allow(rustdoc::redundant_explicit_links)] mod component; +#[cfg(feature = "component-model-async")] +pub(crate) mod concurrent; mod func; mod instance; mod linker; @@ -112,6 +114,11 @@ mod store; pub mod types; mod values; pub use self::component::{Component, ComponentExportIndex}; +#[cfg(feature = "component-model-async")] +pub use self::concurrent::{ + for_any, future, stream, ErrorContext, FutureReader, FutureWriter, Promise, PromisesUnordered, + StreamReader, StreamWriter, +}; pub use self::func::{ ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr, }; @@ -668,3 +675,477 @@ pub mod bindgen_examples; #[cfg(not(any(docsrs, test, doctest)))] #[doc(hidden)] pub mod bindgen_examples {} + +#[cfg(not(feature = "component-model-async"))] +pub(crate) mod concurrent { + use { + crate::{ + component::{ + func::{ComponentType, LiftContext, LowerContext}, + Val, + }, + vm::{VMFuncRef, VMMemoryDefinition, VMOpaqueContext}, + AsContextMut, StoreContextMut, ValRaw, + }, + alloc::{sync::Arc, task::Wake}, + anyhow::Result, + core::{ + future::Future, + marker::PhantomData, + mem::MaybeUninit, + pin::pin, + task::{Context, Poll, Waker}, + }, + wasmtime_environ::component::{ + InterfaceType, RuntimeComponentInstanceIndex, TypeErrorContextTableIndex, + TypeFutureTableIndex, TypeStreamTableIndex, TypeTaskReturnIndex, + }, + }; + + pub fn for_any(fun: F) -> F + where + F: FnOnce(StoreContextMut) -> R + 'static, + R: 'static, + { + fun + } + + fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + Arc::new(DummyWaker).into() + } + + pub(crate) fn poll_and_block<'a, T, R: Send + Sync + 'static>( + mut store: StoreContextMut<'a, T>, + future: impl Future) -> Result + 'static> + + Send + + Sync + + 'static, + _caller_instance: RuntimeComponentInstanceIndex, + ) -> Result<(R, StoreContextMut<'a, T>)> { + match pin!(future).poll(&mut Context::from_waker(&dummy_waker())) { + Poll::Ready(fun) => { + let result = fun(store.as_context_mut())?; + Ok((result, store)) + } + Poll::Pending => { + unreachable!() + } + } + } + + pub(crate) extern "C" fn task_backpressure( + _cx: *mut VMOpaqueContext, + _caller_instance: RuntimeComponentInstanceIndex, + _enabled: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn task_return( + _cx: *mut VMOpaqueContext, + _ty: TypeTaskReturnIndex, + _storage: *mut MaybeUninit, + _storage_len: usize, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn task_wait( + _cx: *mut VMOpaqueContext, + _caller_instance: RuntimeComponentInstanceIndex, + _async_: bool, + _memory: *mut VMMemoryDefinition, + _payload: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn task_poll( + _cx: *mut VMOpaqueContext, + _caller_instance: RuntimeComponentInstanceIndex, + _async_: bool, + _memory: *mut VMMemoryDefinition, + _payload: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn task_yield(_cx: *mut VMOpaqueContext, _async_: bool) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn subtask_drop( + _cx: *mut VMOpaqueContext, + _caller_instance: RuntimeComponentInstanceIndex, + _task_id: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn async_enter( + _cx: *mut VMOpaqueContext, + _start: *mut VMFuncRef, + _return_: *mut VMFuncRef, + _caller_instance: RuntimeComponentInstanceIndex, + _task_return_type: TypeTaskReturnIndex, + _params: u32, + _results: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn async_exit( + _cx: *mut VMOpaqueContext, + _callback: *mut VMFuncRef, + _caller_instance: RuntimeComponentInstanceIndex, + _callee: *mut VMFuncRef, + _callee_instance: RuntimeComponentInstanceIndex, + _param_count: u32, + _result_count: u32, + _flags: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_new( + _vmctx: *mut VMOpaqueContext, + _ty: TypeFutureTableIndex, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_write( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeFutureTableIndex, + _future: u32, + _address: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_read( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeFutureTableIndex, + _future: u32, + _address: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_cancel_write( + _vmctx: *mut VMOpaqueContext, + _ty: TypeFutureTableIndex, + _async_: bool, + _writer: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_cancel_read( + _vmctx: *mut VMOpaqueContext, + _ty: TypeFutureTableIndex, + _async_: bool, + _reader: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn future_close_writable( + _vmctx: *mut VMOpaqueContext, + _ty: TypeFutureTableIndex, + _writer: u32, + _error: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn future_close_readable( + _vmctx: *mut VMOpaqueContext, + _ty: TypeFutureTableIndex, + _reader: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn stream_new( + _vmctx: *mut VMOpaqueContext, + _ty: TypeStreamTableIndex, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn stream_write( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeStreamTableIndex, + _stream: u32, + _address: u32, + _count: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn stream_read( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeStreamTableIndex, + _stream: u32, + _address: u32, + _count: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn stream_cancel_write( + _vmctx: *mut VMOpaqueContext, + _ty: TypeStreamTableIndex, + _async_: bool, + _writer: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn stream_cancel_read( + _vmctx: *mut VMOpaqueContext, + _ty: TypeStreamTableIndex, + _async_: bool, + _reader: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn stream_close_writable( + _vmctx: *mut VMOpaqueContext, + _ty: TypeStreamTableIndex, + _writer: u32, + _error: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn stream_close_readable( + _vmctx: *mut VMOpaqueContext, + _ty: TypeStreamTableIndex, + _reader: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn flat_stream_write( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _ty: TypeStreamTableIndex, + _payload_size: u32, + _payload_align: u32, + _stream: u32, + _address: u32, + _count: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn flat_stream_read( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _ty: TypeStreamTableIndex, + _payload_size: u32, + _payload_align: u32, + _stream: u32, + _address: u32, + _count: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn error_context_new( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeErrorContextTableIndex, + _address: u32, + _count: u32, + ) -> u64 { + unreachable!() + } + + pub(crate) extern "C" fn error_context_debug_message( + _vmctx: *mut VMOpaqueContext, + _memory: *mut VMMemoryDefinition, + _realloc: *mut VMFuncRef, + _string_encoding: u8, + _ty: TypeErrorContextTableIndex, + _handle: u32, + _address: u32, + ) -> bool { + unreachable!() + } + + pub(crate) extern "C" fn error_context_drop( + _vmctx: *mut VMOpaqueContext, + _ty: TypeErrorContextTableIndex, + _error: u32, + ) -> bool { + unreachable!() + } + + pub struct ErrorContext; + + impl ErrorContext { + pub(crate) fn new(_rep: u32) -> Self { + unreachable!() + } + + pub(crate) fn into_val(self) -> Val { + unreachable!() + } + + pub(crate) fn lower( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _dst: &mut MaybeUninit<::Lower>, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn store( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _offset: usize, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn lift( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _src: &::Lower, + ) -> Result { + unreachable!() + } + + pub(crate) fn load( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _bytes: &[u8], + ) -> Result { + unreachable!() + } + } + + pub struct StreamReader

{ + _phantom: PhantomData

, + } + + impl

StreamReader

{ + pub(crate) fn new(_rep: u32) -> Self { + unreachable!() + } + + pub(crate) fn into_val(self) -> Val { + unreachable!() + } + + pub(crate) fn lower( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _dst: &mut MaybeUninit<::Lower>, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn store( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _offset: usize, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn lift( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _src: &::Lower, + ) -> Result { + unreachable!() + } + + pub(crate) fn load( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _bytes: &[u8], + ) -> Result { + unreachable!() + } + } + + pub struct FutureReader

{ + _phantom: PhantomData

, + } + + impl

FutureReader

{ + pub(crate) fn new(_rep: u32) -> Self { + unreachable!() + } + + pub(crate) fn into_val(self) -> Val { + unreachable!() + } + + pub(crate) fn lower( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _dst: &mut MaybeUninit<::Lower>, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn store( + &self, + _cx: &mut LowerContext<'_, T>, + _ty: InterfaceType, + _offset: usize, + ) -> Result<()> { + unreachable!() + } + + pub(crate) fn lift( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _src: &::Lower, + ) -> Result { + unreachable!() + } + + pub(crate) fn load( + _cx: &mut LiftContext<'_>, + _ty: InterfaceType, + _bytes: &[u8], + ) -> Result { + unreachable!() + } + } +} diff --git a/crates/wasmtime/src/runtime/component/storage.rs b/crates/wasmtime/src/runtime/component/storage.rs index 01537b42984d..25e43da8c688 100644 --- a/crates/wasmtime/src/runtime/component/storage.rs +++ b/crates/wasmtime/src/runtime/component/storage.rs @@ -37,7 +37,32 @@ pub unsafe fn slice_to_storage_mut(slice: &mut [MaybeUninit]) -> &mut // stay within the bounds of the number of actual values given rather than // reading past the end of an array. This shouldn't actually trip unless // there's a bug in Wasmtime though. - assert!(mem::size_of_val(slice) >= mem::size_of::()); + assert!( + mem::size_of_val(slice) >= mem::size_of::(), + "needed {}; got {}", + mem::size_of::(), + mem::size_of_val(slice) + ); &mut *slice.as_mut_ptr().cast() } + +/// Same as `storage_as_slice`, but in reverse +#[cfg(feature = "component-model-async")] +pub unsafe fn slice_to_storage(slice: &[ValRaw]) -> &T { + assert_raw_slice_compat::(); + + // This is an actual runtime assertion which if performance calls for we may + // need to relax to a debug assertion. This notably tries to ensure that we + // stay within the bounds of the number of actual values given rather than + // reading past the end of an array. This shouldn't actually trip unless + // there's a bug in Wasmtime though. + assert!( + mem::size_of_val(slice) >= mem::size_of::(), + "needed {}; got {}", + mem::size_of::(), + mem::size_of_val(slice) + ); + + &*slice.as_ptr().cast() +} diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs index 0d63bd664625..f8628094b64d 100644 --- a/crates/wasmtime/src/runtime/component/types.rs +++ b/crates/wasmtime/src/runtime/component/types.rs @@ -7,9 +7,9 @@ use core::fmt; use core::ops::Deref; use wasmtime_environ::component::{ ComponentTypes, InterfaceType, ResourceIndex, TypeComponentIndex, TypeComponentInstanceIndex, - TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex, TypeListIndex, TypeModuleIndex, - TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeTupleIndex, - TypeVariantIndex, + TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex, TypeFutureIndex, TypeFutureTableIndex, + TypeListIndex, TypeModuleIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, + TypeResultIndex, TypeStreamIndex, TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, }; use wasmtime_environ::PrimaryMap; @@ -145,6 +145,16 @@ impl TypeChecker<'_> { (InterfaceType::String, _) => false, (InterfaceType::Char, InterfaceType::Char) => true, (InterfaceType::Char, _) => false, + (InterfaceType::Future(t1), InterfaceType::Future(t2)) => { + self.future_table_types_equal(t1, t2) + } + (InterfaceType::Future(_), _) => false, + (InterfaceType::Stream(t1), InterfaceType::Stream(t2)) => { + self.stream_table_types_equal(t1, t2) + } + (InterfaceType::Stream(_), _) => false, + (InterfaceType::ErrorContext(_), InterfaceType::ErrorContext(_)) => true, + (InterfaceType::ErrorContext(_), _) => false, } } @@ -244,6 +254,30 @@ impl TypeChecker<'_> { let b = &self.b_types[f2]; a.names == b.names } + + fn future_table_types_equal(&self, t1: TypeFutureTableIndex, t2: TypeFutureTableIndex) -> bool { + self.futures_equal(self.a_types[t1].ty, self.b_types[t2].ty) + } + + fn futures_equal(&self, t1: TypeFutureIndex, t2: TypeFutureIndex) -> bool { + let a = &self.a_types[t1]; + let b = &self.b_types[t2]; + match (a.payload, b.payload) { + (Some(t1), Some(t2)) => self.interface_types_equal(t1, t2), + (None, None) => true, + _ => false, + } + } + + fn stream_table_types_equal(&self, t1: TypeStreamTableIndex, t2: TypeStreamTableIndex) -> bool { + self.streams_equal(self.a_types[t1].ty, self.b_types[t2].ty) + } + + fn streams_equal(&self, t1: TypeStreamIndex, t2: TypeStreamIndex) -> bool { + let a = &self.a_types[t1]; + let b = &self.b_types[t2]; + self.interface_types_equal(a.payload, b.payload) + } } /// A `list` interface type @@ -416,7 +450,7 @@ impl PartialEq for OptionType { impl Eq for OptionType {} -/// An `expected` interface type +/// A `result` interface type #[derive(Clone, Debug)] pub struct ResultType(Handle); @@ -476,6 +510,55 @@ impl PartialEq for Flags { impl Eq for Flags {} +/// An `future` interface type +#[derive(Clone, Debug)] +pub struct FutureType(Handle); + +impl FutureType { + pub(crate) fn from(index: TypeFutureIndex, ty: &InstanceType<'_>) -> Self { + FutureType(Handle::new(index, ty)) + } + + /// Retrieve the type parameter for this `future`. + pub fn ty(&self) -> Option { + Some(Type::from( + self.0.types[self.0.index].payload.as_ref()?, + &self.0.instance(), + )) + } +} + +impl PartialEq for FutureType { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::futures_equal) + } +} + +impl Eq for FutureType {} + +/// An `stream` interface type +#[derive(Clone, Debug)] +pub struct StreamType(Handle); + +impl StreamType { + pub(crate) fn from(index: TypeStreamIndex, ty: &InstanceType<'_>) -> Self { + StreamType(Handle::new(index, ty)) + } + + /// Retrieve the type parameter for this `stream`. + pub fn ty(&self) -> Type { + Type::from(&self.0.types[self.0.index].payload, &self.0.instance()) + } +} + +impl PartialEq for StreamType { + fn eq(&self, other: &Self) -> bool { + self.0.equivalent(&other.0, TypeChecker::streams_equal) + } +} + +impl Eq for StreamType {} + /// Represents a component model interface type #[derive(Clone, PartialEq, Eq, Debug)] #[allow(missing_docs)] @@ -503,6 +586,9 @@ pub enum Type { Flags(Flags), Own(ResourceType), Borrow(ResourceType), + Future(FutureType), + Stream(StreamType), + ErrorContext, } impl Type { @@ -660,6 +746,9 @@ impl Type { InterfaceType::Flags(index) => Type::Flags(Flags::from(*index, instance)), InterfaceType::Own(index) => Type::Own(instance.resource_type(*index)), InterfaceType::Borrow(index) => Type::Borrow(instance.resource_type(*index)), + InterfaceType::Future(index) => Type::Future(instance.future_type(*index)), + InterfaceType::Stream(index) => Type::Stream(instance.stream_type(*index)), + InterfaceType::ErrorContext(_) => Type::ErrorContext, } } @@ -688,6 +777,9 @@ impl Type { Type::Flags(_) => "flags", Type::Own(_) => "own", Type::Borrow(_) => "borrow", + Type::Future(_) => "future", + Type::Stream(_) => "stream", + Type::ErrorContext => "error-context", } } } diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs index 15d99847897d..9c539400ea32 100644 --- a/crates/wasmtime/src/runtime/component/values.rs +++ b/crates/wasmtime/src/runtime/component/values.rs @@ -1,3 +1,4 @@ +use crate::component::concurrent::{ErrorContext, FutureReader, StreamReader}; use crate::component::func::{desc, Lift, LiftContext, Lower, LowerContext}; use crate::component::ResourceAny; use crate::prelude::*; @@ -86,6 +87,9 @@ pub enum Val { Result(Result>, Option>>), Flags(Vec), Resource(ResourceAny), + Future(FutureAny), + Stream(StreamAny), + ErrorContext(ErrorContextAny), } impl Val { @@ -198,6 +202,9 @@ impl Val { Val::Flags(flags.into()) } + InterfaceType::Future(_) => FutureReader::<()>::lift(cx, ty, next(src))?.into_val(), + InterfaceType::Stream(_) => StreamReader::<()>::lift(cx, ty, next(src))?.into_val(), + InterfaceType::ErrorContext(_) => ErrorContext::lift(cx, ty, next(src))?.into_val(), }) } @@ -319,6 +326,9 @@ impl Val { } Val::Flags(flags.into()) } + InterfaceType::Future(_) => FutureReader::<()>::load(cx, ty, bytes)?.into_val(), + InterfaceType::Stream(_) => StreamReader::<()>::load(cx, ty, bytes)?.into_val(), + InterfaceType::ErrorContext(_) => ErrorContext::load(cx, ty, bytes)?.into_val(), }) } @@ -429,6 +439,18 @@ impl Val { Ok(()) } (InterfaceType::Flags(_), _) => unexpected(ty, self), + (InterfaceType::Future(_), Val::Future(FutureAny(rep))) => { + FutureReader::<()>::new(*rep).lower(cx, ty, next_mut(dst)) + } + (InterfaceType::Future(_), _) => unexpected(ty, self), + (InterfaceType::Stream(_), Val::Stream(StreamAny(rep))) => { + StreamReader::<()>::new(*rep).lower(cx, ty, next_mut(dst)) + } + (InterfaceType::Stream(_), _) => unexpected(ty, self), + (InterfaceType::ErrorContext(_), Val::ErrorContext(ErrorContextAny(rep))) => { + ErrorContext::new(*rep).lower(cx, ty, next_mut(dst)) + } + (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), } } @@ -564,10 +586,22 @@ impl Val { Ok(()) } (InterfaceType::Flags(_), _) => unexpected(ty, self), + (InterfaceType::Future(_), Val::Future(FutureAny(rep))) => { + FutureReader::<()>::new(*rep).store(cx, ty, offset) + } + (InterfaceType::Future(_), _) => unexpected(ty, self), + (InterfaceType::Stream(_), Val::Stream(StreamAny(rep))) => { + StreamReader::<()>::new(*rep).store(cx, ty, offset) + } + (InterfaceType::Stream(_), _) => unexpected(ty, self), + (InterfaceType::ErrorContext(_), Val::ErrorContext(ErrorContextAny(rep))) => { + ErrorContext::new(*rep).store(cx, ty, offset) + } + (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), } } - fn desc(&self) -> &'static str { + pub(crate) fn desc(&self) -> &'static str { match self { Val::Bool(_) => "bool", Val::U8(_) => "u8", @@ -591,6 +625,9 @@ impl Val { Val::Result(_) => "result", Val::Resource(_) => "resource", Val::Flags(_) => "flags", + Val::Future(_) => "future", + Val::Stream(_) => "stream", + Val::ErrorContext(_) => "error-context", } } @@ -669,6 +706,12 @@ impl PartialEq for Val { (Self::Flags(_), _) => false, (Self::Resource(l), Self::Resource(r)) => l == r, (Self::Resource(_), _) => false, + (Self::Future(l), Self::Future(r)) => l == r, + (Self::Future(_), _) => false, + (Self::Stream(l), Self::Stream(r)) => l == r, + (Self::Stream(_), _) => false, + (Self::ErrorContext(l), Self::ErrorContext(r)) => l == r, + (Self::ErrorContext(_), _) => false, } } } @@ -988,3 +1031,12 @@ fn unexpected(ty: InterfaceType, val: &Val) -> Result { val.desc() ) } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FutureAny(pub(crate) u32); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StreamAny(pub(crate) u32); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ErrorContextAny(pub(crate) u32); diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 5cfda2e19983..896506972056 100644 --- a/crates/wasmtime/src/runtime/store.rs +++ b/crates/wasmtime/src/runtime/store.rs @@ -76,6 +76,8 @@ //! contents of `StoreOpaque`. This is an invariant that we, as the authors of //! `wasmtime`, must uphold for the public interface to be safe. +#[cfg(feature = "component-model-async")] +use crate::component::concurrent; use crate::hash_set::HashSet; use crate::instance::InstanceData; use crate::linker::Definition; @@ -226,6 +228,8 @@ pub struct StoreInner { Option) -> Result + Send + Sync>>, // for comments about `ManuallyDrop`, see `Store::into_data` data: ManuallyDrop, + #[cfg(feature = "component-model-async")] + concurrent_state: concurrent::ConcurrentState, } enum ResourceLimiterInner { @@ -592,6 +596,8 @@ impl Store { call_hook: None, epoch_deadline_behavior: None, data: ManuallyDrop::new(data), + #[cfg(feature = "component-model-async")] + concurrent_state: Default::default(), }); // Wasmtime uses the callee argument to host functions to learn about @@ -1106,6 +1112,16 @@ impl<'a, T> StoreContextMut<'a, T> { self.0.data_mut() } + #[cfg(feature = "component-model-async")] + pub(crate) fn concurrent_state(&mut self) -> &mut concurrent::ConcurrentState { + self.0.concurrent_state() + } + + #[cfg(feature = "component-model-async")] + pub(crate) fn has_pkey(&self) -> bool { + self.0.pkey.is_some() + } + /// Returns the underlying [`Engine`] this store is connected to. pub fn engine(&self) -> &Engine { self.0.engine() @@ -1191,6 +1207,11 @@ impl StoreInner { &mut self.data } + #[cfg(feature = "component-model-async")] + fn concurrent_state(&mut self) -> &mut concurrent::ConcurrentState { + &mut self.concurrent_state + } + #[inline] pub fn call_hook(&mut self, s: CallHook) -> Result<()> { if self.inner.pkey.is_none() && self.call_hook.is_none() { diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index 813416e134b0..feec2f007a17 100644 --- a/crates/wasmtime/src/runtime/vm/component.rs +++ b/crates/wasmtime/src/runtime/vm/component.rs @@ -29,8 +29,10 @@ const INVALID_PTR: usize = 0xdead_dead_beef_beef_u64 as usize; mod libcalls; mod resources; +mod states; pub use self::resources::{CallContexts, ResourceTable, ResourceTables}; +pub use self::states::StateTable; /// Runtime representation of a component instance and all state necessary for /// the instance itself. @@ -58,6 +60,9 @@ pub struct ComponentInstance { /// is how this field is manipulated. component_resource_tables: PrimaryMap, + component_waitable_tables: PrimaryMap>, + component_error_context_tables: PrimaryMap>, + /// Storage for the type information about resources within this component /// instance. /// @@ -83,6 +88,7 @@ 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 (sub)component instance of the caller. /// * `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 @@ -91,6 +97,7 @@ pub struct ComponentInstance { /// 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. /// * `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 @@ -102,7 +109,7 @@ pub struct ComponentInstance { /// or not. On failure this function records trap information in TLS which /// should be suitable for reading later. // -// FIXME: 9 arguments is probably too many. The `data` through `string-encoding` +// 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. @@ -110,10 +117,12 @@ pub type VMLoweringCallee = extern "C" fn( vmctx: *mut VMOpaqueContext, data: *mut u8, ty: u32, + caller_instance: u32, flags: *mut u8, opt_memory: *mut VMMemoryDefinition, opt_realloc: *mut VMFuncRef, string_encoding: u8, + async_: u8, args_and_results: *mut mem::MaybeUninit, nargs_and_results: usize, ) -> bool; @@ -130,6 +139,181 @@ pub struct VMLowering { pub data: *mut u8, } +/// Type signature for the host-defined `task.backpressure` built-in function. +pub type VMTaskBackpressureCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + arg: u32, +) -> bool; + +/// Type signature for the host-defined `task.return` built-in function. +pub type VMTaskReturnCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + ty: TypeTaskReturnIndex, + args_and_results: *mut mem::MaybeUninit, + nargs_and_results: usize, +) -> bool; + +/// Type signature for the host-defined `task.wait` and `task.poll` built-in functions. +pub type VMTaskWaitOrPollCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + caller_instance: RuntimeComponentInstanceIndex, + async_: bool, + memory: *mut VMMemoryDefinition, + payload: u32, +) -> u64; + +/// Type signature for the host-defined `task.yield` built-in function. +pub type VMTaskYieldCallback = extern "C" fn(vmctx: *mut VMOpaqueContext, async_: bool) -> bool; + +/// Type signature for the host-defined `subtask.drop` built-in function. +pub type VMSubtaskDropCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + instance: RuntimeComponentInstanceIndex, + arg: u32, +) -> bool; + +/// Type signature for the host-defined built-in function to represent starting +/// a call to an async-lowered import in a FACT-generated module. +pub type VMAsyncEnterCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + start: *mut VMFuncRef, + return_: *mut VMFuncRef, + caller_instance: RuntimeComponentInstanceIndex, + task_return_type: TypeTaskReturnIndex, + params: u32, + results: u32, +) -> bool; + +/// Type signature for the host-defined built-in function to represent +/// completing a call to an async-lowered import in a FACT-generated module. +pub type VMAsyncExitCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + callback: *mut VMFuncRef, + caller_instance: RuntimeComponentInstanceIndex, + callee: *mut VMFuncRef, + callee_instance: RuntimeComponentInstanceIndex, + param_count: u32, + result_count: u32, + flags: u32, +) -> u64; + +/// Type signature for the host-defined `future.new` built-in function. +pub type VMFutureNewCallback = + extern "C" fn(vmctx: *mut VMOpaqueContext, ty: TypeFutureTableIndex) -> u64; + +/// Type signature for the host-defined `future.read` and `future.write` +/// built-in functions. +pub type VMFutureTransmitCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeFutureTableIndex, + future: u32, + address: u32, +) -> u64; + +/// Type signature for the host-defined `future.cancel-read` and +/// `future.cancel-write` built-in functions. +pub type VMFutureCancelCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + async_: bool, + handle: u32, +) -> u64; + +/// Type signature for the host-defined `future.close-readable` built-in function. +pub type VMFutureCloseReadableCallback = + extern "C" fn(vmctx: *mut VMOpaqueContext, ty: TypeFutureTableIndex, handle: u32) -> bool; + +/// Type signature for the host-defined `future.close-writable` built-in function. +pub type VMFutureCloseWritableCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + ty: TypeFutureTableIndex, + handle: u32, + error: u32, +) -> bool; + +/// Type signature for the host-defined `stream.new` built-in function. +pub type VMStreamNewCallback = + extern "C" fn(vmctx: *mut VMOpaqueContext, ty: TypeStreamTableIndex) -> u64; + +/// Type signature for the host-defined `stream.read` and `stream.write` +/// built-in functions +pub type VMStreamTransmitCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeStreamTableIndex, + stream: u32, + address: u32, + count: u32, +) -> u64; + +/// Type signature for the host-defined `stream.read` ans `stream.write` +/// built-in functions for when the payload is trivially `memcpy`-able. +pub type VMFlatStreamTransmitCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + ty: TypeStreamTableIndex, + payload_size: u32, + payload_align: u32, + stream: u32, + address: u32, + count: u32, +) -> u64; + +/// Type signature for the host-defined `stream.close-readable` built-in function. +pub type VMStreamCloseReadableCallback = + extern "C" fn(vmctx: *mut VMOpaqueContext, ty: TypeStreamTableIndex, handle: u32) -> bool; + +/// Type signature for the host-defined `stream.close-writable` built-in function. +pub type VMStreamCloseWritableCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + handle: u32, + error: u32, +) -> bool; + +/// Type signature for the host-defined `stream.cancel-read` and +/// `stream.cancel-write` built-in functions. +pub type VMStreamCancelCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + ty: TypeStreamTableIndex, + async_: bool, + handle: u32, +) -> u64; + +/// Type signature for the host-defined `error-context.new` built-in function. +pub type VMErrorContextNewCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeErrorContextTableIndex, + address: u32, + count: u32, +) -> u64; + +/// Type signature for the host-defined `error-context.debug-message` built-in +/// function. +pub type VMErrorContextDebugMessageCallback = extern "C" fn( + vmctx: *mut VMOpaqueContext, + memory: *mut VMMemoryDefinition, + realloc: *mut VMFuncRef, + string_encoding: u8, + ty: TypeErrorContextTableIndex, + handle: u32, + address: u32, +) -> bool; + +/// Type signature for the host-defined `error-context.drop` built-in function. +pub type VMErrorContextDropCallback = + extern "C" fn(vmctx: *mut VMOpaqueContext, ty: TypeErrorContextTableIndex, handle: u32) -> bool; + /// This is a marker type to represent the underlying allocation of a /// `VMComponentContext`. /// @@ -148,6 +332,30 @@ pub struct VMComponentContext { _marker: marker::PhantomPinned, } +/// Represents the state of a stream or future handle. +#[derive(Debug, Eq, PartialEq)] +pub enum StreamFutureState { + /// Both the read and write ends are owned by the same component instance. + Local, + /// Only the write end is owned by this component instance. + Write, + /// Only the read end is owned by this component instance. + Read, + /// A read or write is in progress. + Busy, +} + +/// Represents the state of a waitable handle. +#[derive(Debug)] +pub enum WaitableState { + /// Represents a task handle. + Task, + /// Represents a stream handle. + Stream(TypeStreamTableIndex, StreamFutureState), + /// Represents a future handle. + Future(TypeFutureTableIndex, StreamFutureState), +} + impl ComponentInstance { /// Converts the `vmctx` provided into a `ComponentInstance` and runs the /// provided closure with that instance. @@ -197,12 +405,26 @@ impl ComponentInstance { ) { assert!(alloc_size >= Self::alloc_layout(&offsets).size()); - let num_tables = runtime_info.component().num_resource_tables; - let mut component_resource_tables = PrimaryMap::with_capacity(num_tables); - for _ in 0..num_tables { + let num_resource_tables = runtime_info.component().num_resource_tables; + let mut component_resource_tables = PrimaryMap::with_capacity(num_resource_tables); + for _ in 0..num_resource_tables { component_resource_tables.push(ResourceTable::default()); } + let num_waitable_tables = runtime_info.component().num_runtime_component_instances; + let mut component_waitable_tables = + PrimaryMap::with_capacity(usize::try_from(num_waitable_tables).unwrap()); + for _ in 0..num_waitable_tables { + component_waitable_tables.push(StateTable::default()); + } + + let num_error_context_tables = runtime_info.component().num_error_context_tables; + let mut component_error_context_tables = + PrimaryMap::with_capacity(num_error_context_tables); + for _ in 0..num_error_context_tables { + component_error_context_tables.push(StateTable::default()); + } + ptr::write( ptr.as_ptr(), ComponentInstance { @@ -216,6 +438,8 @@ impl ComponentInstance { .unwrap(), ), component_resource_tables, + component_waitable_tables, + component_error_context_tables, runtime_info, resource_types, vmctx: VMComponentContext { @@ -290,6 +514,18 @@ impl ComponentInstance { } } + /// Returns the async callback pointer corresponding to the index provided. + /// + /// This can only be called after `idx` has been initialized at runtime + /// during the instantiation process of a component. + pub fn runtime_callback(&self, idx: RuntimeCallbackIndex) -> NonNull { + unsafe { + let ret = *self.vmctx_plus_offset::>(self.offsets.runtime_callback(idx)); + debug_assert!(ret.as_ptr() as usize != INVALID_PTR); + ret + } + } + /// Returns the post-return pointer corresponding to the index provided. /// /// This can only be called after `idx` has been initialized at runtime @@ -363,6 +599,15 @@ impl ComponentInstance { } } + /// Same as `set_runtime_memory` but for async callback function pointers. + pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull) { + unsafe { + let storage = self.vmctx_plus_offset_mut(self.offsets.runtime_callback(idx)); + debug_assert!(*storage as usize == INVALID_PTR); + *storage = ptr.as_ptr(); + } + } + /// Same as `set_runtime_memory` but for post-return function pointers. pub fn set_runtime_post_return( &mut self, @@ -439,6 +684,75 @@ impl ComponentInstance { } } + /// Set the host-provided callbacks for various async-, future-, stream-, + /// and error-context-related built-in functions. + pub fn set_async_callbacks( + &mut self, + task_backpressure: VMTaskBackpressureCallback, + task_return: VMTaskReturnCallback, + task_wait: VMTaskWaitOrPollCallback, + task_poll: VMTaskWaitOrPollCallback, + task_yield: VMTaskYieldCallback, + subtask_drop: VMSubtaskDropCallback, + async_enter: VMAsyncEnterCallback, + async_exit: VMAsyncExitCallback, + future_new: VMFutureNewCallback, + future_write: VMFutureTransmitCallback, + future_read: VMFutureTransmitCallback, + future_cancel_write: VMFutureCancelCallback, + future_cancel_read: VMFutureCancelCallback, + future_close_writable: VMFutureCloseWritableCallback, + future_close_readable: VMFutureCloseReadableCallback, + stream_new: VMStreamNewCallback, + stream_write: VMStreamTransmitCallback, + stream_read: VMStreamTransmitCallback, + stream_cancel_write: VMStreamCancelCallback, + stream_cancel_read: VMStreamCancelCallback, + stream_close_writable: VMStreamCloseWritableCallback, + stream_close_readable: VMStreamCloseReadableCallback, + flat_stream_write: VMFlatStreamTransmitCallback, + flat_stream_read: VMFlatStreamTransmitCallback, + error_context_new: VMErrorContextNewCallback, + error_context_debug_message: VMErrorContextDebugMessageCallback, + error_context_drop: VMErrorContextDropCallback, + ) { + unsafe { + *self.vmctx_plus_offset_mut(self.offsets.task_backpressure()) = task_backpressure; + *self.vmctx_plus_offset_mut(self.offsets.task_return()) = task_return; + *self.vmctx_plus_offset_mut(self.offsets.task_wait()) = task_wait; + *self.vmctx_plus_offset_mut(self.offsets.task_poll()) = task_poll; + *self.vmctx_plus_offset_mut(self.offsets.task_yield()) = task_yield; + *self.vmctx_plus_offset_mut(self.offsets.subtask_drop()) = subtask_drop; + *self.vmctx_plus_offset_mut(self.offsets.async_enter()) = async_enter; + *self.vmctx_plus_offset_mut(self.offsets.async_exit()) = async_exit; + *self.vmctx_plus_offset_mut(self.offsets.future_new()) = future_new; + *self.vmctx_plus_offset_mut(self.offsets.future_write()) = future_write; + *self.vmctx_plus_offset_mut(self.offsets.future_read()) = future_read; + *self.vmctx_plus_offset_mut(self.offsets.future_cancel_write()) = future_cancel_write; + *self.vmctx_plus_offset_mut(self.offsets.future_cancel_read()) = future_cancel_read; + *self.vmctx_plus_offset_mut(self.offsets.future_close_writable()) = + future_close_writable; + *self.vmctx_plus_offset_mut(self.offsets.future_close_readable()) = + future_close_readable; + *self.vmctx_plus_offset_mut(self.offsets.stream_new()) = stream_new; + *self.vmctx_plus_offset_mut(self.offsets.stream_write()) = stream_write; + *self.vmctx_plus_offset_mut(self.offsets.stream_read()) = stream_read; + *self.vmctx_plus_offset_mut(self.offsets.stream_cancel_write()) = stream_cancel_write; + *self.vmctx_plus_offset_mut(self.offsets.stream_cancel_read()) = stream_cancel_read; + *self.vmctx_plus_offset_mut(self.offsets.stream_close_writable()) = + stream_close_writable; + *self.vmctx_plus_offset_mut(self.offsets.stream_close_readable()) = + stream_close_readable; + *self.vmctx_plus_offset_mut(self.offsets.flat_stream_write()) = flat_stream_write; + *self.vmctx_plus_offset_mut(self.offsets.flat_stream_read()) = flat_stream_read; + *self.vmctx_plus_offset_mut(self.offsets.error_context_debug_message()) = + error_context_new; + *self.vmctx_plus_offset_mut(self.offsets.error_context_debug_message()) = + error_context_debug_message; + *self.vmctx_plus_offset_mut(self.offsets.error_context_drop()) = error_context_drop; + } + } + unsafe fn initialize_vmctx(&mut self, store: *mut dyn VMStore) { *self.vmctx_plus_offset_mut(self.offsets.magic()) = VMCOMPONENT_MAGIC; *self.vmctx_plus_offset_mut(self.offsets.builtins()) = &libcalls::VMComponentBuiltins::INIT; @@ -453,7 +767,7 @@ impl ComponentInstance { } // In debug mode set non-null bad values to all "pointer looking" bits - // and pices related to lowering and such. This'll help detect any + // 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. if cfg!(debug_assertions) { @@ -479,6 +793,11 @@ impl ComponentInstance { let offset = self.offsets.runtime_realloc(i); *self.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.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); @@ -573,6 +892,22 @@ impl ComponentInstance { &mut self.component_resource_tables } + /// Retrieves the tables for tracking waitable handles and their states with respect + /// to the components which own them. + pub fn component_waitable_tables( + &mut self, + ) -> &mut PrimaryMap> { + &mut self.component_waitable_tables + } + + /// Retrieves the tables for tracking error-context handles and their reference + /// counts with respect to the components which own them. + pub fn component_error_context_tables( + &mut self, + ) -> &mut PrimaryMap> { + &mut self.component_error_context_tables + } + /// Returns the destructor and instance flags for the specified resource /// table type. /// @@ -633,6 +968,113 @@ impl ComponentInstance { pub(crate) fn resource_exit_call(&mut self) -> Result<()> { self.resource_tables().exit_call() } + + pub(crate) fn future_transfer( + &mut self, + src_idx: u32, + src: TypeFutureTableIndex, + dst: TypeFutureTableIndex, + ) -> Result { + let src_instance = self.component_types()[src].instance; + let dst_instance = self.component_types()[dst].instance; + let [src_table, dst_table] = self + .component_waitable_tables + .get_many_mut([src_instance, dst_instance]) + .unwrap(); + let (rep, WaitableState::Future(src_ty, src_state)) = + src_table.get_mut_by_index(src_idx)? + else { + bail!("invalid future handle"); + }; + if *src_ty != src { + bail!("invalid future handle"); + } + match src_state { + StreamFutureState::Local => { + *src_state = StreamFutureState::Write; + assert!(dst_table.get_mut_by_rep(rep).is_none()); + dst_table.insert(rep, WaitableState::Future(dst, StreamFutureState::Read)) + } + StreamFutureState::Read => { + src_table.remove_by_index(src_idx)?; + if let Some((dst_idx, dst_state)) = dst_table.get_mut_by_rep(rep) { + let WaitableState::Future(dst_ty, dst_state) = dst_state else { + unreachable!(); + }; + assert_eq!(*dst_ty, dst); + assert_eq!(*dst_state, StreamFutureState::Write); + *dst_state = StreamFutureState::Local; + Ok(dst_idx) + } else { + dst_table.insert(rep, WaitableState::Future(dst, StreamFutureState::Read)) + } + } + StreamFutureState::Write => bail!("cannot transfer write end of future"), + StreamFutureState::Busy => bail!("cannot transfer busy future"), + } + } + + pub(crate) fn stream_transfer( + &mut self, + src_idx: u32, + src: TypeStreamTableIndex, + dst: TypeStreamTableIndex, + ) -> Result { + let src_instance = self.component_types()[src].instance; + let dst_instance = self.component_types()[dst].instance; + let [src_table, dst_table] = self + .component_waitable_tables + .get_many_mut([src_instance, dst_instance]) + .unwrap(); + let (rep, WaitableState::Stream(src_ty, src_state)) = + src_table.get_mut_by_index(src_idx)? + else { + bail!("invalid stream handle"); + }; + if *src_ty != src { + bail!("invalid stream handle"); + } + match src_state { + StreamFutureState::Local => { + *src_state = StreamFutureState::Write; + assert!(dst_table.get_mut_by_rep(rep).is_none()); + dst_table.insert(rep, WaitableState::Stream(dst, StreamFutureState::Read)) + } + StreamFutureState::Read => { + src_table.remove_by_index(src_idx)?; + if let Some((dst_idx, dst_state)) = dst_table.get_mut_by_rep(rep) { + let WaitableState::Stream(dst_ty, dst_state) = dst_state else { + unreachable!(); + }; + assert_eq!(*dst_ty, dst); + assert_eq!(*dst_state, StreamFutureState::Write); + *dst_state = StreamFutureState::Local; + Ok(dst_idx) + } else { + dst_table.insert(rep, WaitableState::Stream(dst, StreamFutureState::Read)) + } + } + StreamFutureState::Write => bail!("cannot transfer write end of stream"), + StreamFutureState::Busy => bail!("cannot transfer busy stream"), + } + } + + pub(crate) fn error_context_transfer( + &mut self, + src_idx: u32, + src: TypeErrorContextTableIndex, + dst: TypeErrorContextTableIndex, + ) -> Result { + let (rep, _) = self.component_error_context_tables[src].get_mut_by_index(src_idx)?; + let dst = &mut self.component_error_context_tables[dst]; + + if let Some((dst_idx, dst_state)) = dst.get_mut_by_rep(rep) { + *dst_state += 1; + Ok(dst_idx) + } else { + dst.insert(rep, 1) + } + } } impl VMComponentContext { @@ -653,7 +1095,7 @@ impl VMComponentContext { /// This type can be dereferenced to `ComponentInstance` to access the /// underlying methods. pub struct OwnedComponentInstance { - ptr: SendSyncPtr, + pub(crate) ptr: SendSyncPtr, } impl OwnedComponentInstance { @@ -716,6 +1158,11 @@ impl OwnedComponentInstance { unsafe { self.instance_mut().set_runtime_realloc(idx, ptr) } } + /// See `ComponentInstance::set_runtime_callback` + pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull) { + unsafe { self.instance_mut().set_runtime_callback(idx, ptr) } + } + /// See `ComponentInstance::set_runtime_post_return` pub fn set_runtime_post_return( &mut self, @@ -757,6 +1204,70 @@ impl OwnedComponentInstance { pub fn resource_types_mut(&mut self) -> &mut Arc { unsafe { &mut (*self.ptr.as_ptr()).resource_types } } + + /// See `ComponentInstance::set_async_callbacks` + pub fn set_async_callbacks( + &mut self, + task_backpressure: VMTaskBackpressureCallback, + task_return: VMTaskReturnCallback, + task_wait: VMTaskWaitOrPollCallback, + task_poll: VMTaskWaitOrPollCallback, + task_yield: VMTaskYieldCallback, + subtask_drop: VMSubtaskDropCallback, + async_enter: VMAsyncEnterCallback, + async_exit: VMAsyncExitCallback, + future_new: VMFutureNewCallback, + future_write: VMFutureTransmitCallback, + future_read: VMFutureTransmitCallback, + future_cancel_write: VMFutureCancelCallback, + future_cancel_read: VMFutureCancelCallback, + future_close_writable: VMFutureCloseWritableCallback, + future_close_readable: VMFutureCloseReadableCallback, + stream_new: VMStreamNewCallback, + stream_write: VMStreamTransmitCallback, + stream_read: VMStreamTransmitCallback, + stream_cancel_write: VMStreamCancelCallback, + stream_cancel_read: VMStreamCancelCallback, + stream_close_writable: VMStreamCloseWritableCallback, + stream_close_readable: VMStreamCloseReadableCallback, + flat_stream_write: VMFlatStreamTransmitCallback, + flat_stream_read: VMFlatStreamTransmitCallback, + error_context_new: VMErrorContextNewCallback, + error_context_debug_message: VMErrorContextDebugMessageCallback, + error_context_drop: VMErrorContextDropCallback, + ) { + unsafe { + self.instance_mut().set_async_callbacks( + task_backpressure, + task_return, + task_wait, + task_poll, + task_yield, + subtask_drop, + async_enter, + async_exit, + future_new, + future_write, + future_read, + future_cancel_write, + future_cancel_read, + future_close_writable, + future_close_readable, + stream_new, + stream_write, + stream_read, + stream_cancel_write, + stream_cancel_read, + stream_close_writable, + stream_close_readable, + flat_stream_write, + flat_stream_read, + error_context_new, + error_context_debug_message, + error_context_drop, + ) + } + } } impl Deref for OwnedComponentInstance { diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 94d83babe75f..ef3b6f60b236 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -6,7 +6,9 @@ use crate::runtime::vm::HostResultHasUnwindSentinel; use core::cell::Cell; use core::convert::Infallible; use core::slice; -use wasmtime_environ::component::TypeResourceTableIndex; +use wasmtime_environ::component::{ + TypeErrorContextTableIndex, TypeFutureTableIndex, TypeResourceTableIndex, TypeStreamTableIndex, +}; const UTF16_TAG: usize = 1 << 31; @@ -557,3 +559,42 @@ unsafe fn resource_exit_call(vmctx: *mut VMComponentContext) -> Result<()> { unsafe fn trap(_vmctx: *mut VMComponentContext, code: u8) -> Result { Err(wasmtime_environ::Trap::from_u8(code).unwrap().into()) } + +unsafe fn future_transfer( + vmctx: *mut VMComponentContext, + src_idx: u32, + src_table: u32, + dst_table: u32, +) -> Result { + let src_table = TypeFutureTableIndex::from_u32(src_table); + let dst_table = TypeFutureTableIndex::from_u32(dst_table); + ComponentInstance::from_vmctx(vmctx, |instance| { + instance.future_transfer(src_idx, src_table, dst_table) + }) +} + +unsafe fn stream_transfer( + vmctx: *mut VMComponentContext, + src_idx: u32, + src_table: u32, + dst_table: u32, +) -> Result { + let src_table = TypeStreamTableIndex::from_u32(src_table); + let dst_table = TypeStreamTableIndex::from_u32(dst_table); + ComponentInstance::from_vmctx(vmctx, |instance| { + instance.stream_transfer(src_idx, src_table, dst_table) + }) +} + +unsafe fn error_context_transfer( + vmctx: *mut VMComponentContext, + src_idx: u32, + src_table: u32, + dst_table: u32, +) -> Result { + let src_table = TypeErrorContextTableIndex::from_u32(src_table); + let dst_table = TypeErrorContextTableIndex::from_u32(dst_table); + ComponentInstance::from_vmctx(vmctx, |instance| { + instance.error_context_transfer(src_idx, src_table, dst_table) + }) +} diff --git a/crates/wasmtime/src/runtime/vm/component/states.rs b/crates/wasmtime/src/runtime/vm/component/states.rs new file mode 100644 index 000000000000..8a8a9a39ee6a --- /dev/null +++ b/crates/wasmtime/src/runtime/vm/component/states.rs @@ -0,0 +1,126 @@ +use { + alloc::vec::Vec, + anyhow::{bail, Result}, + core::mem, +}; + +/// The maximum handle value is specified in +/// +/// currently and keeps the upper bit free for use in the component. +const MAX_HANDLE: u32 = 1 << 30; + +enum Slot { + Free { next: u32 }, + Occupied { rep: u32, state: T }, +} + +pub struct StateTable { + next: u32, + slots: Vec>, + // TODO: This is a sparse table (where zero means "no entry"); it might make + // more sense to use a `HashMap` here, but we'd need one that's + // no_std-compatible. A `BTreeMap` might also be appropriate if we restrict + // ourselves to `alloc::collections`. + reps_to_indexes: Vec, +} + +impl Default for StateTable { + fn default() -> Self { + Self { + next: 0, + slots: Vec::new(), + reps_to_indexes: Vec::new(), + } + } +} + +impl StateTable { + pub fn insert(&mut self, rep: u32, state: T) -> Result { + if matches!(self + .reps_to_indexes + .get(usize::try_from(rep).unwrap()), Some(idx) if *idx != 0) + { + bail!("rep {rep} already exists in this table"); + } + + let next = self.next as usize; + if next == self.slots.len() { + self.slots.push(Slot::Free { + next: self.next.checked_add(1).unwrap(), + }); + } + let ret = self.next; + self.next = match mem::replace(&mut self.slots[next], Slot::Occupied { rep, state }) { + Slot::Free { next } => next, + _ => unreachable!(), + }; + // The component model reserves index 0 as never allocatable so add one + // to the table index to start the numbering at 1 instead. Also note + // that the component model places an upper-limit per-table on the + // maximum allowed index. + let ret = ret + 1; + if ret >= MAX_HANDLE { + bail!("cannot allocate another handle: index overflow"); + } + + let rep = usize::try_from(rep).unwrap(); + if self.reps_to_indexes.len() <= rep { + self.reps_to_indexes.resize(rep.checked_add(1).unwrap(), 0); + } + + self.reps_to_indexes[rep] = ret; + + Ok(ret) + } + + fn handle_index_to_table_index(&self, idx: u32) -> Option { + // NB: `idx` is decremented by one to account for the `+1` above during + // allocation. + let idx = idx.checked_sub(1)?; + usize::try_from(idx).ok() + } + + fn get_mut(&mut self, idx: u32) -> Result<&mut Slot> { + let slot = self + .handle_index_to_table_index(idx) + .and_then(|i| self.slots.get_mut(i)); + match slot { + None | Some(Slot::Free { .. }) => bail!("unknown handle index {idx}"), + Some(slot) => Ok(slot), + } + } + + pub fn get_mut_by_index(&mut self, idx: u32) -> Result<(u32, &mut T)> { + let slot = self + .handle_index_to_table_index(idx) + .and_then(|i| self.slots.get_mut(i)); + match slot { + None | Some(Slot::Free { .. }) => bail!("unknown handle index {idx}"), + Some(Slot::Occupied { rep, state }) => Ok((*rep, state)), + } + } + + pub fn get_mut_by_rep(&mut self, rep: u32) -> Option<(u32, &mut T)> { + let index = *self.reps_to_indexes.get(usize::try_from(rep).unwrap())?; + if index > 0 { + let (_, state) = self.get_mut_by_index(index).unwrap(); + Some((index, state)) + } else { + None + } + } + + pub fn remove_by_index(&mut self, idx: u32) -> Result<(u32, T)> { + let to_fill = Slot::Free { next: self.next }; + let Slot::Occupied { rep, state } = mem::replace(self.get_mut(idx)?, to_fill) else { + unreachable!() + }; + self.next = idx - 1; + { + let rep = usize::try_from(rep).unwrap(); + assert_eq!(idx, self.reps_to_indexes[rep]); + self.reps_to_indexes[rep] = 0; + } + Ok((rep, state)) + } +} diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs index f16ed6f7f516..ddf8b84f9055 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs @@ -519,6 +519,7 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator { LowerImport { .. } | ExtractMemory(_) | ExtractRealloc(_) + | ExtractCallback(_) | ExtractPostReturn(_) | Resource(_) => {} } diff --git a/crates/wasmtime/src/runtime/vm/interpreter.rs b/crates/wasmtime/src/runtime/vm/interpreter.rs index fbe7a8cd3ea5..329afd3d98b1 100644 --- a/crates/wasmtime/src/runtime/vm/interpreter.rs +++ b/crates/wasmtime/src/runtime/vm/interpreter.rs @@ -326,7 +326,7 @@ impl InterpreterRef<'_> { use wasmtime_environ::component::ComponentBuiltinFunctionIndex; if id == const { HostCall::ComponentLowerImport.index() } { - call!(@host VMLoweringCallee(ptr, ptr, u32, ptr, ptr, ptr, u8, ptr, size) -> bool); + call!(@host VMLoweringCallee(ptr, ptr, u32, u32, ptr, ptr, ptr, u8, u8, ptr, size) -> bool); } macro_rules! component { diff --git a/crates/wasmtime/src/runtime/wave/component.rs b/crates/wasmtime/src/runtime/wave/component.rs index b770b6bf4109..656a03136d1d 100644 --- a/crates/wasmtime/src/runtime/wave/component.rs +++ b/crates/wasmtime/src/runtime/wave/component.rs @@ -41,7 +41,11 @@ impl WasmType for component::Type { Self::Result(_) => WasmTypeKind::Result, Self::Flags(_) => WasmTypeKind::Flags, - Self::Own(_) | Self::Borrow(_) => WasmTypeKind::Unsupported, + Self::Own(_) + | Self::Borrow(_) + | Self::Stream(_) + | Self::Future(_) + | Self::ErrorContext => WasmTypeKind::Unsupported, } } @@ -134,7 +138,9 @@ impl WasmValue for component::Val { Self::Option(_) => WasmTypeKind::Option, Self::Result(_) => WasmTypeKind::Result, Self::Flags(_) => WasmTypeKind::Flags, - Self::Resource(_) => WasmTypeKind::Unsupported, + Self::Resource(_) | Self::Stream(_) | Self::Future(_) | Self::ErrorContext(_) => { + WasmTypeKind::Unsupported + } } } diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs index 8a7f19dc08d0..1346a7f11361 100644 --- a/crates/wast/src/component.rs +++ b/crates/wast/src/component.rs @@ -284,6 +284,9 @@ fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { Val::Result(..) => "result", Val::Flags(..) => "flags", Val::Resource(..) => "resource", + Val::Future(..) => "future", + Val::Stream(..) => "stream", + Val::ErrorContext(..) => "error-context", }; bail!("expected `{expected}` got `{actual}`") } diff --git a/crates/wit-bindgen/Cargo.toml b/crates/wit-bindgen/Cargo.toml index 90e8ea3e9959..9e958081ce96 100644 --- a/crates/wit-bindgen/Cargo.toml +++ b/crates/wit-bindgen/Cargo.toml @@ -20,3 +20,4 @@ indexmap = { workspace = true } [features] std = [] +component-model-async = ['std'] diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 254a9aa7e79d..61f2dd2c338c 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -59,10 +59,10 @@ struct Wasmtime { opts: Opts, /// A list of all interfaces which were imported by this world. /// - /// The second value here is the contents of the module that this interface - /// generated. The third value is the name of the interface as also present - /// in `self.interface_names`. - import_interfaces: Vec<(InterfaceId, String, InterfaceName)>, + /// 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_functions: Vec, exports: Exports, types: Types, @@ -134,6 +134,21 @@ pub struct Opts { /// 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, @@ -175,6 +190,15 @@ pub struct Opts { /// Path to the `wasmtime` crate if it's not the default path. pub wasmtime_crate: Option, + + /// If true, write the generated bindings to a file for better error + /// messages from `rustc`. + /// + /// 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 + /// ways), whereas this option lets you specify it on a case-by-case basis. + pub debug: bool, } #[derive(Debug, Clone)] @@ -213,28 +237,10 @@ pub enum AsyncConfig { OnlyImports(HashSet), } -impl AsyncConfig { - pub fn is_import_async(&self, f: &str) -> bool { - match self { - AsyncConfig::None => false, - AsyncConfig::All => true, - AsyncConfig::AllExceptImports(set) => !set.contains(f), - AsyncConfig::OnlyImports(set) => set.contains(f), - } - } - - pub fn is_drop_async(&self, r: &str) -> bool { - self.is_import_async(&format!("[drop]{r}")) - } - - pub fn maybe_async(&self) -> bool { - match self { - AsyncConfig::None => false, - AsyncConfig::All | AsyncConfig::AllExceptImports(_) | AsyncConfig::OnlyImports(_) => { - true - } - } - } +pub enum CallStyle { + Sync, + Async, + Concurrent, } #[derive(Default, Debug, Clone)] @@ -260,6 +266,22 @@ impl TrappableImports { impl Opts { pub fn generate(&self, resolve: &Resolve, world: WorldId) -> anyhow::Result { + // TODO: Should we refine this test to inspect only types reachable from + // the specified world? + if !cfg!(feature = "component-model-async") + && resolve.types.iter().any(|(_, ty)| { + matches!( + ty.kind, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext + ) + }) + { + anyhow::bail!( + "must enable `component-model-async` feature when using WIT files \ + containing future, stream, or error types" + ); + } + let mut r = Wasmtime::default(); r.sizes.fill(resolve); r.opts = self.clone(); @@ -268,7 +290,41 @@ impl Opts { } fn is_store_data_send(&self) -> bool { - self.async_.maybe_async() || self.require_store_data_send + 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 + } + } + } } } @@ -455,7 +511,7 @@ impl Wasmtime { // resource-related functions get their trait signatures // during `type_resource`. let sig = if let FunctionKind::Freestanding = func.kind { - gen.generate_function_trait_sig(func); + gen.generate_function_trait_sig(func, "Data"); Some(mem::take(&mut gen.src).into()) } else { None @@ -471,14 +527,14 @@ impl Wasmtime { WorldItem::Interface { id, .. } => { gen.gen.interface_last_seen_as_import.insert(*id, true); gen.current_interface = Some((*id, name, false)); - let snake = match name { + let snake = to_rust_ident(&match name { WorldKey::Name(s) => s.to_snake_case(), WorldKey::Interface(id) => resolve.interfaces[*id] .name .as_ref() .unwrap() .to_snake_case(), - }; + }); let module = if gen.gen.name_interface(resolve, *id, name, false) { // If this interface is remapped then that means that it was // provided via the `with` key in the bindgen configuration. @@ -523,8 +579,12 @@ impl Wasmtime { " ) }; - self.import_interfaces - .push((*id, module, self.interface_names[id].clone())); + self.import_interfaces.push(( + name.clone(), + *id, + module, + self.interface_names[id].clone(), + )); let interface_path = self.import_interface_path(id); self.interface_link_options[id] @@ -806,10 +866,11 @@ fn _new( 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_) = if self.opts.async_.maybe_async() { - ("async", "_async", "where _T: Send", ".await") - } else { - ("", "", "", "") + let (async_, async__, bounds, await_) = match self.opts.call_style() { + CallStyle::Async | CallStyle::Concurrent => { + ("async", "_async", ": Send + 'static", ".await") + } + CallStyle::Sync => ("", "", ": 'static", ""), }; uwriteln!( self.src, @@ -836,7 +897,7 @@ impl Clone for {camel}Pre {{ }} }} -impl<_T> {camel}Pre<_T> {{ +impl<_T{bounds}> {camel}Pre<_T> {{ /// Creates a new copy of `{camel}Pre` bindings which can then /// be used to instantiate into a particular store. /// @@ -866,7 +927,6 @@ impl<_T> {camel}Pre<_T> {{ &self, mut store: impl {wt}::AsContextMut, ) -> {wt}::Result<{camel}> - {where_clause} {{ let mut store = store.as_context_mut(); let instance = self.instance_pre.instantiate{async__}(&mut store){await_}?; @@ -1031,7 +1091,7 @@ impl<_T> {camel}Pre<_T> {{ component: &{wt}::component::Component, linker: &{wt}::component::Linker<_T>, ) -> {wt}::Result<{camel}> - {where_clause} + where _T{bounds} {{ let pre = linker.instantiate_pre(component)?; {camel}Pre::new(pre)?.instantiate{async__}(store){await_} @@ -1090,7 +1150,12 @@ impl<_T> {camel}Pre<_T> {{ } let imports = mem::take(&mut self.import_interfaces); - self.emit_modules(imports); + self.emit_modules( + imports + .into_iter() + .map(|(_, id, module, path)| (id, module, path)) + .collect(), + ); let exports = mem::take(&mut self.exports.modules); self.emit_modules(exports); @@ -1357,7 +1422,7 @@ impl Wasmtime { let wt = self.wasmtime_path(); let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name); - if self.opts.async_.maybe_async() { + if let CallStyle::Async = self.opts.call_style() { uwriteln!( self.src, "#[{wt}::component::__internal::trait_variant_make(::core::marker::Send)]" @@ -1365,7 +1430,7 @@ impl Wasmtime { } uwrite!(self.src, "pub trait {world_camel}Imports"); let mut supertraits = vec![]; - if self.opts.async_.maybe_async() { + if let CallStyle::Async = self.opts.call_style() { supertraits.push("Send".to_string()); } for (_, name) in get_world_resources(resolve, world) { @@ -1407,16 +1472,31 @@ impl Wasmtime { ); // Generate impl WorldImports for &mut WorldImports - let maybe_send = if self.opts.async_.maybe_async() { + let maybe_send = if let CallStyle::Async = self.opts.call_style() { "+ Send" } else { "" }; if !self.opts.skip_mut_forwarding_impls { + let maybe_maybe_sized = if let CallStyle::Concurrent = self.opts.call_style() { + "" + } else { + "+ ?Sized" + }; uwriteln!( self.src, - "impl<_T: {world_camel}Imports + ?Sized {maybe_send}> {world_camel}Imports for &mut _T {{" + "impl<_T: {world_camel}Imports {maybe_maybe_sized} {maybe_send}> {world_camel}Imports for &mut _T {{" ); + let has_concurrent_function = self.import_functions.iter().any(|f| { + matches!( + self.opts.import_call_style(None, &f.func.name), + CallStyle::Concurrent + ) + }); + + if has_concurrent_function { + self.src.push_str("type Data = _T::Data;\n"); + } // Forward each method call to &mut T for f in self.import_functions.iter() { if let Some(sig) = &f.sig { @@ -1430,7 +1510,7 @@ impl Wasmtime { uwrite!(self.src, "{},", to_rust_ident(name)); } uwrite!(self.src, ")"); - if self.opts.async_.is_import_async(&f.func.name) { + if let CallStyle::Async = self.opts.import_call_style(None, &f.func.name) { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); @@ -1443,7 +1523,7 @@ impl Wasmtime { fn import_interface_paths(&self) -> Vec<(InterfaceId, String)> { self.import_interfaces .iter() - .map(|(id, _, name)| { + .map(|(_, id, _, name)| { let path = match name { InterfaceName::Path(path) => path.join("::"), InterfaceName::Remapped { name_at_root, .. } => name_at_root.clone(), @@ -1470,7 +1550,7 @@ impl Wasmtime { let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name); traits.push(format!("{world_camel}Imports")); } - if self.opts.async_.maybe_async() { + if let CallStyle::Async = self.opts.call_style() { traits.push("Send".to_string()); } traits @@ -1512,6 +1592,7 @@ impl Wasmtime { 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, @@ -1528,7 +1609,45 @@ impl Wasmtime { uwriteln!(self.src, "Ok(())\n}}"); } - let host_bounds = format!("U: {}", self.world_host_traits(resolve, world).join(" + ")); + let (host_bounds, data_bounds) = if let CallStyle::Concurrent = self.opts.call_style() { + // TODO: include world imports trait if applicable + let bounds = self + .import_interfaces + .iter() + .map(|(key, id, _, name)| { + ( + key, + id, + match name { + InterfaceName::Path(path) => path.join("::"), + InterfaceName::Remapped { name_at_root, .. } => name_at_root.clone(), + }, + ) + }) + .map(|(key, id, path)| { + format!( + " + {path}::Host{}", + concurrent_constraints( + resolve, + &self.opts, + Some(&resolve.name_world_key(key)), + *id + )("T") + ) + }) + .collect::>() + .concat(); + + ( + format!("U: Send{bounds}"), + format!("T: Send{bounds} + 'static,"), + ) + } else { + ( + format!("U: {}", self.world_host_traits(resolve, world).join(" + ")), + data_bounds.to_string(), + ) + }; if !self.opts.skip_mut_forwarding_impls { uwriteln!( @@ -1584,6 +1703,7 @@ impl Wasmtime { } fn generate_add_resource_to_linker( + qualifier: Option<&str>, src: &mut Source, opts: &Opts, wt: &str, @@ -1593,7 +1713,7 @@ impl Wasmtime { ) { let gate = FeatureGate::open(src, stability); let camel = name.to_upper_camel_case(); - if opts.async_.is_drop_async(name) { + if let CallStyle::Async = opts.drop_call_style(qualifier, name) { uwriteln!( src, "{inst}.resource_async( @@ -1664,8 +1784,9 @@ impl<'a> InterfaceGenerator<'a> { TypeDefKind::Result(r) => self.type_result(id, name, r, &ty.docs), TypeDefKind::List(t) => self.type_list(id, name, t, &ty.docs), TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs), - TypeDefKind::Future(_) => todo!("generate for future"), - TypeDefKind::Stream(_) => todo!("generate for stream"), + TypeDefKind::Future(_) => panic!("future types need not be defined"), + TypeDefKind::Stream(_) => panic!("stream types need not be defined"), + TypeDefKind::ErrorContext => panic!("the error-context type needs not be defined"), TypeDefKind::Handle(handle) => self.type_handle(id, name, handle, &ty.docs), TypeDefKind::Resource => self.type_resource(id, name, ty, &ty.docs), TypeDefKind::Unknown => unreachable!(), @@ -1709,13 +1830,14 @@ impl<'a> InterfaceGenerator<'a> { } // Generate resource trait - if self.gen.opts.async_.maybe_async() { + if let CallStyle::Async = self.gen.opts.call_style() { uwriteln!( self.src, "#[{wt}::component::__internal::trait_variant_make(::core::marker::Send)]" ) } - uwriteln!(self.src, "pub trait Host{camel} {{"); + + uwriteln!(self.src, "pub trait Host{camel}: Sized {{"); let mut functions = match resource.owner { TypeOwner::World(id) => self.resolve.worlds[id] @@ -1742,12 +1864,29 @@ impl<'a> InterfaceGenerator<'a> { | FunctionKind::Constructor(resource) => id == resource, }); + let has_concurrent_function = functions.iter().any(|func| { + matches!( + self.gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name), + CallStyle::Concurrent + ) + }); + + if has_concurrent_function { + uwriteln!(self.src, "type {camel}Data;"); + } + for func in &functions { - self.generate_function_trait_sig(func); + self.generate_function_trait_sig(func, &format!("{camel}Data")); self.push_str(";\n"); } - if self.gen.opts.async_.is_drop_async(name) { + if let CallStyle::Async = self + .gen + .opts + .drop_call_style(self.qualifier().as_deref(), name) + { uwrite!(self.src, "async "); } uwrite!( @@ -1759,32 +1898,56 @@ impl<'a> InterfaceGenerator<'a> { // Generate impl HostResource for &mut HostResource if !self.gen.opts.skip_mut_forwarding_impls { - let maybe_send = if self.gen.opts.async_.maybe_async() { + let maybe_send = if let CallStyle::Async = self.gen.opts.call_style() { "+ Send" } else { "" }; + let maybe_maybe_sized = if has_concurrent_function { + "" + } else { + "+ ?Sized" + }; uwriteln!( self.src, - "impl <_T: Host{camel} + ?Sized {maybe_send}> Host{camel} for &mut _T {{" + "impl <_T: Host{camel} {maybe_maybe_sized} {maybe_send}> Host{camel} for &mut _T {{" ); + if has_concurrent_function { + uwriteln!(self.src, "type {camel}Data = _T::{camel}Data;"); + } for func in &functions { - self.generate_function_trait_sig(func); - uwrite!( - self.src, - "{{ Host{camel}::{}(*self,", - rust_function_name(func) - ); + let call_style = self + .gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name); + self.generate_function_trait_sig(func, &format!("{camel}Data")); + if let CallStyle::Concurrent = call_style { + uwrite!( + self.src, + "{{ <_T as Host{camel}>::{}(store,", + rust_function_name(func) + ); + } else { + uwrite!( + self.src, + "{{ Host{camel}::{}(*self,", + rust_function_name(func) + ); + } for (name, _) in func.params.iter() { uwrite!(self.src, "{},", to_rust_ident(name)); } uwrite!(self.src, ")"); - if self.gen.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = call_style { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); } - if self.gen.opts.async_.is_drop_async(name) { + if let CallStyle::Async = self + .gen + .opts + .drop_call_style(self.qualifier().as_deref(), name) + { uwriteln!(self.src, " async fn drop(&mut self, rep: {wt}::component::Resource<{camel}>) -> {wt}::Result<()> {{ Host{camel}::drop(*self, rep).await @@ -2087,10 +2250,9 @@ impl<'a> InterfaceGenerator<'a> { self.push_str( "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n", ); - self.push_str("write!(f, \"{:?}\", self)"); + self.push_str("write!(f, \"{:?}\", self)\n"); self.push_str("}\n"); self.push_str("}\n"); - self.push_str("\n"); if cfg!(feature = "std") { self.push_str("impl"); @@ -2342,6 +2504,26 @@ impl<'a> InterfaceGenerator<'a> { } } + fn print_result_ty_tuple(&mut self, results: &Results, mode: TypeMode) { + self.push_str("("); + match results { + Results::Named(rs) if rs.is_empty() => self.push_str(")"), + Results::Named(rs) => { + for (i, (_, ty)) in rs.iter().enumerate() { + if i > 0 { + self.push_str(", ") + } + self.print_ty(ty, mode) + } + self.push_str(")"); + } + Results::Anon(ty) => { + self.print_ty(ty, mode); + self.push_str(",)"); + } + } + } + fn special_case_trappable_error( &mut self, func: &Function, @@ -2384,7 +2566,7 @@ impl<'a> InterfaceGenerator<'a> { let owner = TypeOwner::Interface(id); let wt = self.gen.wasmtime_path(); - let is_maybe_async = self.gen.opts.async_.maybe_async(); + let is_maybe_async = matches!(self.gen.opts.call_style(), CallStyle::Async); if is_maybe_async { uwriteln!( self.src, @@ -2394,24 +2576,45 @@ impl<'a> InterfaceGenerator<'a> { // Generate the `pub trait` which represents the host functionality for // this import which additionally inherits from all resource traits // for this interface defined by `type_resource`. + uwrite!(self.src, "pub trait Host"); let mut host_supertraits = vec![]; if is_maybe_async { host_supertraits.push("Send".to_string()); } + let mut saw_resources = false; for (_, name) in get_resources(self.resolve, id) { + saw_resources = true; host_supertraits.push(format!("Host{}", name.to_upper_camel_case())); } + if saw_resources { + host_supertraits.push("Sized".to_string()); + } if !host_supertraits.is_empty() { uwrite!(self.src, ": {}", host_supertraits.join(" + ")); } uwriteln!(self.src, " {{"); + + let has_concurrent_function = iface.functions.iter().any(|(_, func)| { + matches!(func.kind, FunctionKind::Freestanding) + && matches!( + self.gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name), + CallStyle::Concurrent + ) + }); + + if has_concurrent_function { + self.push_str("type Data;\n"); + } + for (_, func) in iface.functions.iter() { match func.kind { FunctionKind::Freestanding => {} _ => continue, } - self.generate_function_trait_sig(func); + self.generate_function_trait_sig(func, "Data"); self.push_str(";\n"); } @@ -2459,13 +2662,32 @@ impl<'a> InterfaceGenerator<'a> { } uwriteln!(self.src, "}}"); - let (data_bounds, mut host_bounds) = if self.gen.opts.is_store_data_send() { - ("T: Send,", "Host + Send".to_string()) - } else { - ("", "Host".to_string()) + let (data_bounds, mut host_bounds, mut get_host_bounds) = match self.gen.opts.call_style() { + CallStyle::Async => ( + "T: Send,".to_string(), + "Host + Send".to_string(), + "Host + Send".to_string(), + ), + CallStyle::Concurrent => { + let constraints = concurrent_constraints( + self.resolve, + &self.gen.opts, + self.qualifier().as_deref(), + id, + ); + + ( + "T: Send + 'static,".to_string(), + format!("Host{} + Send", constraints("T")), + format!("Host{} + Send", constraints("D")), + ) + } + CallStyle::Sync => (String::new(), "Host".to_string(), "Host".to_string()), }; + for ty in required_conversion_traits { uwrite!(host_bounds, " + {ty}"); + uwrite!(get_host_bounds, " + {ty}"); } let (options_param, options_arg) = if self.gen.interface_link_options[&id].has_any() { @@ -2477,28 +2699,28 @@ impl<'a> InterfaceGenerator<'a> { uwriteln!( self.src, " - pub trait GetHost: - Fn(T) -> >::Host + pub trait GetHost: + Fn(T) -> >::Host + Send + Sync + Copy + 'static {{ - type Host: {host_bounds}; + type Host: {get_host_bounds}; }} - impl GetHost for F + impl GetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, - O: {host_bounds}, + O: {get_host_bounds}, {{ type Host = O; }} - pub fn add_to_linker_get_host( + pub fn add_to_linker_get_host GetHost<&'a mut T, T, Host: {host_bounds}>>( linker: &mut {wt}::component::Linker, {options_param} - host_getter: impl for<'a> GetHost<&'a mut T>, + host_getter: G, ) -> {wt}::Result<()> where {data_bounds} {{ @@ -2509,6 +2731,7 @@ impl<'a> InterfaceGenerator<'a> { for (ty, name) in get_resources(self.resolve, id) { Wasmtime::generate_add_resource_to_linker( + self.qualifier().as_deref(), &mut self.src, &self.gen.opts, &wt, @@ -2546,23 +2769,46 @@ impl<'a> InterfaceGenerator<'a> { // Generate impl Host for &mut Host let maybe_send = if is_maybe_async { "+ Send" } else { "" }; + let maybe_maybe_sized = if has_concurrent_function { + "" + } else { + "+ ?Sized" + }; + uwriteln!( self.src, - "impl<_T: Host + ?Sized {maybe_send}> Host for &mut _T {{" + "impl<_T: Host {maybe_maybe_sized} {maybe_send}> Host for &mut _T {{" ); + + if has_concurrent_function { + self.push_str("type Data = _T::Data;\n"); + } + // Forward each method call to &mut T for (_, func) in iface.functions.iter() { match func.kind { FunctionKind::Freestanding => {} _ => continue, } - self.generate_function_trait_sig(func); - uwrite!(self.src, "{{ Host::{}(*self,", rust_function_name(func)); + let call_style = self + .gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name); + self.generate_function_trait_sig(func, "Data"); + if let CallStyle::Concurrent = call_style { + uwrite!( + self.src, + "{{ <_T as Host>::{}(store,", + rust_function_name(func) + ); + } else { + uwrite!(self.src, "{{ Host::{}(*self,", rust_function_name(func)); + } for (name, _) in func.params.iter() { uwrite!(self.src, "{},", to_rust_ident(name)); } uwrite!(self.src, ")"); - if self.gen.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = call_style { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); @@ -2582,15 +2828,24 @@ impl<'a> InterfaceGenerator<'a> { } } + fn qualifier(&self) -> Option { + self.current_interface + .map(|(_, key, _)| self.resolve.name_world_key(key)) + } + fn generate_add_function_to_linker(&mut self, owner: TypeOwner, func: &Function, linker: &str) { let gate = FeatureGate::open(&mut self.src, &func.stability); uwrite!( self.src, "{linker}.{}(\"{}\", ", - if self.gen.opts.async_.is_import_async(&func.name) { - "func_wrap_async" - } else { - "func_wrap" + match self + .gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name) + { + CallStyle::Sync => "func_wrap", + CallStyle::Async => "func_wrap_async", + CallStyle::Concurrent => "func_wrap_concurrent", }, func.name ); @@ -2614,16 +2869,20 @@ impl<'a> InterfaceGenerator<'a> { self.src.push_str(") : ("); for (_, ty) in func.params.iter() { - // Lift is required to be impled for this type, so we can't use + // Lift is required to be implied for this type, so we can't use // a borrowed type: self.print_ty(ty, TypeMode::Owned); self.src.push_str(", "); } - self.src.push_str(") |"); - self.src.push_str(" {\n"); + self.src.push_str(")| {\n"); + + let style = self + .gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name); if self.gen.opts.tracing { - if self.gen.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = style { self.src.push_str("use tracing::Instrument;\n"); } @@ -2649,7 +2908,7 @@ impl<'a> InterfaceGenerator<'a> { ); } - if self.gen.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = &style { uwriteln!( self.src, " {wt}::component::__internal::Box::new(async move {{ " @@ -2681,8 +2940,11 @@ impl<'a> InterfaceGenerator<'a> { ); } - self.src - .push_str("let host = &mut host_getter(caller.data_mut());\n"); + self.src.push_str(if let CallStyle::Concurrent = &style { + "let host = caller;\n" + } else { + "let host = &mut host_getter(caller.data_mut());\n" + }); let func_name = rust_function_name(func); let host_trait = match func.kind { FunctionKind::Freestanding => match owner { @@ -2701,15 +2963,32 @@ impl<'a> InterfaceGenerator<'a> { format!("Host{resource}") } }; - uwrite!(self.src, "let r = {host_trait}::{func_name}(host, "); + + if let CallStyle::Concurrent = &style { + uwrite!( + self.src, + "let r = ::{func_name}(host, " + ); + } else { + uwrite!(self.src, "let r = {host_trait}::{func_name}(host, "); + } for (i, _) in func.params.iter().enumerate() { uwrite!(self.src, "arg{},", i); } - if self.gen.opts.async_.is_import_async(&func.name) { - uwrite!(self.src, ").await;\n"); - } else { - uwrite!(self.src, ");\n"); + self.src.push_str(match &style { + CallStyle::Sync | CallStyle::Concurrent => ");\n", + CallStyle::Async => ").await;\n", + }); + + if let CallStyle::Concurrent = &style { + self.src.push_str( + "Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + ", + ); } if self.gen.opts.tracing { @@ -2751,29 +3030,53 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "r\n"); } - if self.gen.opts.async_.is_import_async(&func.name) { - // Need to close Box::new and async block - - if self.gen.opts.tracing { - self.src.push_str("}.instrument(span))\n"); - } else { - self.src.push_str("})\n"); + match &style { + CallStyle::Sync => (), + CallStyle::Async => { + if self.gen.opts.tracing { + self.src.push_str("}.instrument(span))\n"); + } else { + self.src.push_str("})\n"); + } + } + CallStyle::Concurrent => { + let old_source = mem::take(&mut self.src); + self.print_result_ty_tuple(&func.results, TypeMode::Owned); + let result_type = String::from(mem::replace(&mut self.src, old_source)); + let box_fn = format!( + "Box) -> \ + wasmtime::Result<{result_type}> + Send + Sync>" + ); + uwriteln!( + self.src, + " }}) as {box_fn} + }}) as ::std::pin::Pin \ + + Send + Sync + 'static>> + " + ); } } - self.src.push_str("}\n"); } - fn generate_function_trait_sig(&mut self, func: &Function) { + fn generate_function_trait_sig(&mut self, func: &Function, data: &str) { let wt = self.gen.wasmtime_path(); self.rustdoc(&func.docs); - if self.gen.opts.async_.is_import_async(&func.name) { + let style = self + .gen + .opts + .import_call_style(self.qualifier().as_deref(), &func.name); + if let CallStyle::Async = &style { self.push_str("async "); } self.push_str("fn "); self.push_str(&rust_function_name(func)); - self.push_str("(&mut self, "); + self.push_str(&if let CallStyle::Concurrent = &style { + format!("(store: wasmtime::StoreContextMut<'_, Self::{data}>, ") + } else { + "(&mut self, ".to_string() + }); for (name, param) in func.params.iter() { let name = to_rust_ident(name); self.push_str(&name); @@ -2784,6 +3087,10 @@ impl<'a> InterfaceGenerator<'a> { self.push_str(")"); self.push_str(" -> "); + if let CallStyle::Concurrent = &style { + uwrite!(self.src, "impl ::std::future::Future) -> "); + } + if !self.gen.opts.trappable_imports.can_trap(func) { self.print_result_ty(&func.results, TypeMode::Owned); } else if let Some((r, _id, error_typename)) = self.special_case_trappable_error(func) { @@ -2806,6 +3113,10 @@ impl<'a> InterfaceGenerator<'a> { self.print_result_ty(&func.results, TypeMode::Owned); self.push_str(">"); } + + if let CallStyle::Concurrent = &style { + self.push_str(" + Send + Sync + 'static> + Send + Sync + 'static where Self: Sized"); + } } fn extract_typed_function(&mut self, func: &Function) -> (String, String) { @@ -2836,12 +3147,16 @@ impl<'a> InterfaceGenerator<'a> { ) { // Exports must be async if anything could be async, it's just imports // that get to be optionally async/sync. - let is_async = self.gen.opts.async_.maybe_async(); - - let (async_, async__, await_) = if is_async { - ("async", "_async", ".await") - } else { - ("", "", "") + let style = self.gen.opts.call_style(); + let (async_, async__, await_, concurrent) = match &style { + CallStyle::Async | CallStyle::Concurrent => { + if self.gen.opts.concurrent_exports { + ("async", "INVALID", "INVALID", true) + } else { + ("async", "_async", ".await", false) + } + } + CallStyle::Sync => ("", "", "", false), }; self.rustdoc(&func.docs); @@ -2853,23 +3168,35 @@ impl<'a> InterfaceGenerator<'a> { func.item_name().to_snake_case(), ); + let param_mode = if let CallStyle::Concurrent = &style { + TypeMode::Owned + } else { + TypeMode::AllBorrowed("'_") + }; + for (i, param) in func.params.iter().enumerate() { uwrite!(self.src, "arg{}: ", i); - self.print_ty(¶m.1, TypeMode::AllBorrowed("'_")); + self.print_ty(¶m.1, param_mode); self.push_str(","); } uwrite!(self.src, ") -> {wt}::Result<"); + if concurrent { + uwrite!(self.src, "{wt}::component::Promise<"); + } self.print_result_ty(&func.results, TypeMode::Owned); - - if is_async { - uwriteln!(self.src, "> where ::Data: Send {{"); - } else { - self.src.push_str("> {\n"); + if concurrent { + uwrite!(self.src, ">"); } - if self.gen.opts.tracing { - if is_async { + uwrite!( + self.src, + "> where ::Data: Send + 'static {{\n" + ); + + // TODO: support tracing concurrent calls + if self.gen.opts.tracing && !concurrent { + if let CallStyle::Async = &style { self.src.push_str("use tracing::Instrument;\n"); } @@ -2889,7 +3216,7 @@ impl<'a> InterfaceGenerator<'a> { func.name, )); - if !is_async { + if !matches!(&style, CallStyle::Async) { self.src.push_str( " let _enter = span.enter(); @@ -2901,7 +3228,7 @@ impl<'a> InterfaceGenerator<'a> { self.src.push_str("let callee = unsafe {\n"); uwrite!(self.src, "{wt}::component::TypedFunc::<("); for (_, ty) in func.params.iter() { - self.print_ty(ty, TypeMode::AllBorrowed("'_")); + self.print_ty(ty, param_mode); self.push_str(", "); } self.src.push_str("), ("); @@ -2919,46 +3246,65 @@ impl<'a> InterfaceGenerator<'a> { func_field_name(self.resolve, func), ); self.src.push_str("};\n"); - self.src.push_str("let ("); - for (i, _) in func.results.iter_types().enumerate() { - uwrite!(self.src, "ret{},", i); - } - uwrite!( - self.src, - ") = callee.call{async__}(store.as_context_mut(), (" - ); - for (i, _) in func.params.iter().enumerate() { - uwrite!(self.src, "arg{}, ", i); - } - let instrument = if is_async && self.gen.opts.tracing { - ".instrument(span.clone())" - } else { - "" - }; - uwriteln!(self.src, ")){instrument}{await_}?;"); - - let instrument = if is_async && self.gen.opts.tracing { - ".instrument(span)" - } else { - "" - }; - uwriteln!( - self.src, - "callee.post_return{async__}(store.as_context_mut()){instrument}{await_}?;" - ); + if concurrent { + uwrite!( + self.src, + "let promise = callee.call_concurrent(store.as_context_mut(), (" + ); + for (i, _) in func.params.iter().enumerate() { + uwrite!(self.src, "arg{i}, "); + } + self.src.push_str(")).await?;"); - self.src.push_str("Ok("); - if func.results.iter_types().len() == 1 { - self.src.push_str("ret0"); + if func.results.iter_types().len() == 1 { + self.src.push_str("Ok(promise.map(|(v,)| v))\n"); + } else { + self.src.push_str("Ok(promise)"); + } } else { - self.src.push_str("("); + self.src.push_str("let ("); for (i, _) in func.results.iter_types().enumerate() { uwrite!(self.src, "ret{},", i); } - self.src.push_str(")"); + uwrite!( + self.src, + ") = callee.call{async__}(store.as_context_mut(), (" + ); + for (i, _) in func.params.iter().enumerate() { + uwrite!(self.src, "arg{}, ", i); + } + + let instrument = if matches!(&style, CallStyle::Async) && self.gen.opts.tracing { + ".instrument(span.clone())" + } else { + "" + }; + uwriteln!(self.src, ")){instrument}{await_}?;"); + + let instrument = if matches!(&style, CallStyle::Async) && self.gen.opts.tracing { + ".instrument(span)" + } else { + "" + }; + + uwriteln!( + self.src, + "callee.post_return{async__}(store.as_context_mut()){instrument}{await_}?;" + ); + + self.src.push_str("Ok("); + if func.results.iter_types().len() == 1 { + self.src.push_str("ret0"); + } else { + self.src.push_str("("); + for (i, _) in func.results.iter_types().enumerate() { + uwrite!(self.src, "ret{},", i); + } + self.src.push_str(")"); + } + self.src.push_str(")\n"); } - self.src.push_str(")\n"); // End function body self.src.push_str("}\n"); @@ -3239,8 +3585,9 @@ fn type_contains_lists(ty: Type, resolve: &Resolve) -> bool { | TypeDefKind::Unknown | TypeDefKind::Flags(_) | TypeDefKind::Handle(_) - | TypeDefKind::Enum(_) => false, - TypeDefKind::Option(ty) => type_contains_lists(*ty, resolve), + | TypeDefKind::Enum(_) + | TypeDefKind::ErrorContext => false, + TypeDefKind::Option(ty) | TypeDefKind::Stream(ty) => type_contains_lists(*ty, resolve), TypeDefKind::Result(Result_ { ok, err }) => { option_type_contains_lists(*ok, resolve) || option_type_contains_lists(*err, resolve) @@ -3259,10 +3606,6 @@ fn type_contains_lists(ty: Type, resolve: &Resolve) -> bool { .any(|case| option_type_contains_lists(case.ty, resolve)), TypeDefKind::Type(ty) => type_contains_lists(*ty, resolve), TypeDefKind::Future(ty) => option_type_contains_lists(*ty, resolve), - TypeDefKind::Stream(Stream { element, end }) => { - option_type_contains_lists(*element, resolve) - || option_type_contains_lists(*end, resolve) - } TypeDefKind::List(_) => true, }, @@ -3354,3 +3697,61 @@ fn get_world_resources<'a>( _ => None, }) } + +fn concurrent_constraints<'a>( + resolve: &'a Resolve, + opts: &Opts, + qualifier: Option<&str>, + id: InterfaceId, +) -> impl Fn(&str) -> String + 'a { + let has_concurrent_function = resolve.interfaces[id].functions.iter().any(|(_, func)| { + matches!(func.kind, FunctionKind::Freestanding) + && matches!( + opts.import_call_style(qualifier, &func.name), + CallStyle::Concurrent + ) + }); + + let types = resolve.interfaces[id] + .types + .iter() + .filter_map(|(name, ty)| match resolve.types[*ty].kind { + TypeDefKind::Resource + if resolve.interfaces[id] + .functions + .values() + .any(|func| match func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(resource) + | FunctionKind::Static(resource) + | FunctionKind::Constructor(resource) => { + *ty == resource + && matches!( + opts.import_call_style(qualifier, &func.name), + CallStyle::Concurrent + ) + } + }) => + { + Some(format!("{}Data", name.to_upper_camel_case())) + } + _ => None, + }) + .chain(has_concurrent_function.then_some("Data".to_string())) + .collect::>(); + + move |v| { + if types.is_empty() { + String::new() + } else { + format!( + "<{}>", + types + .iter() + .map(|s| format!("{s} = {v}")) + .collect::>() + .join(", ") + ) + } + } +} diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs index d676d095f60d..3792f24666af 100644 --- a/crates/wit-bindgen/src/rust.rs +++ b/crates/wit-bindgen/src/rust.rs @@ -120,7 +120,7 @@ pub trait RustGenerator<'a> { needs_generics(resolve, &resolve.types[*t].kind) } TypeDefKind::Type(Type::String) => true, - TypeDefKind::Type(_) => false, + TypeDefKind::Type(_) | TypeDefKind::ErrorContext => false, TypeDefKind::Unknown => unreachable!(), } } @@ -166,17 +166,18 @@ pub trait RustGenerator<'a> { panic!("unsupported anonymous type reference: enum") } TypeDefKind::Future(ty) => { - self.push_str("Future<"); - self.print_optional_ty(ty.as_ref(), mode); + self.push_str("wasmtime::component::FutureReader<"); + self.print_optional_ty(ty.as_ref(), TypeMode::Owned); self.push_str(">"); } - TypeDefKind::Stream(stream) => { - self.push_str("Stream<"); - self.print_optional_ty(stream.element.as_ref(), mode); - self.push_str(","); - self.print_optional_ty(stream.end.as_ref(), mode); + TypeDefKind::Stream(ty) => { + self.push_str("wasmtime::component::StreamReader<"); + self.print_ty(ty, TypeMode::Owned); self.push_str(">"); } + TypeDefKind::ErrorContext => { + self.push_str("wasmtime::component::ErrorContext"); + } TypeDefKind::Handle(handle) => { self.print_handle(handle); diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs index c45d3d80f159..b29c0da728c1 100644 --- a/crates/wit-bindgen/src/types.rs +++ b/crates/wit-bindgen/src/types.rs @@ -148,10 +148,7 @@ impl Types { info = self.type_info(resolve, ty); info.has_list = true; } - TypeDefKind::Type(ty) => { - info = self.type_info(resolve, ty); - } - TypeDefKind::Option(ty) => { + TypeDefKind::Type(ty) | TypeDefKind::Option(ty) | TypeDefKind::Stream(ty) => { info = self.type_info(resolve, ty); } TypeDefKind::Result(r) => { @@ -161,12 +158,8 @@ impl Types { TypeDefKind::Future(ty) => { info = self.optional_type_info(resolve, ty.as_ref()); } - TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); - } TypeDefKind::Handle(_) => info.has_handle = true, - TypeDefKind::Resource => {} + TypeDefKind::Resource | TypeDefKind::ErrorContext => {} TypeDefKind::Unknown => unreachable!(), } self.type_info.insert(ty, info); diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs index a27fd52df6e2..a6417b07d3a2 100644 --- a/tests/all/component_model/dynamic.rs +++ b/tests/all/component_model/dynamic.rs @@ -87,7 +87,7 @@ fn primitives() -> Result<()> { .call_and_post_return(&mut store, &output, &mut []) .unwrap_err(); assert!( - err.to_string().contains("expected 1 results(s), got 0"), + err.to_string().contains("expected 1 result(s), got 0"), "{err}" ); diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index 1fbb138c8c02..c8545e549675 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -880,7 +880,7 @@ fn component_instance_size_limit() -> Result<()> { Ok(_) => panic!("should have hit limit"), Err(e) => assert_eq!( e.to_string(), - "instance allocation for this component requires 64 bytes of `VMComponentContext` space \ + "instance allocation for this component requires 272 bytes of `VMComponentContext` space \ which exceeds the configured maximum of 1 bytes" ), }