From ce6395f598ffa3058fc4a160c43f8353d87061d5 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 fix async tests when `component-model-async` enabled Enabling this feature for all tests revealed various missing pieces in the new `concurrent.rs` fiber mechanism, which I've addressed. This adds a bunch of ugly `#[cfg(feature = "component-model-async")]` guards, but those will all go away once I unify the two async fiber implementations. Signed-off-by: Joel Dice add and modify tests to cover concurrent APIs Primarily, this tests and implements cases where parameters and/or results must be passed via linear memory instead of the stack. Signed-off-by: Joel Dice `concurrent_{imports|exports}` component macro codegen tests This enables codegen testing of the `concurrent_imports` and `concurrent_exports` options to `wasmtime::component::bindgen` and also fixes code generation for world-level function and resource exports that use the concurrent call style. Signed-off-by: Joel Dice `concurrent_{imports|exports}` component macro expanded tests This enables testing of the `concurrent_imports` and `concurrent_exports` options in `crates/component-macro/tests/expanded.rs`. Signed-off-by: Joel Dice add tests/misc_testsuite/component-model-async/*.wast These only test instantiation of components which use various async options and built-ins so far. Next, I'll happy and sad path tests which actually execute code. Signed-off-by: Joel Dice appease clippy Signed-off-by: Joel Dice add tests/misc_testsuite/component-model-async/fused.wast Signed-off-by: Joel Dice add non-panicking bounds checks where appropriate Signed-off-by: Joel Dice remove post-return bits from async result lift code ...at least until we've determined whether post-return options even make sense for async-lifted exports. Signed-off-by: Joel Dice fix component-model-async/fused.wast test failure Signed-off-by: Joel Dice use `enum` types to represent status and event codes Signed-off-by: Joel Dice fix component-model-async/fused.wast test failure (2nd try) Signed-off-by: Joel Dice use `gc_types = true` in component-model-async/fused.wast We use `Instruction::RefFunc` when generating adapters for async lifts and/or lowers, which Winch doesn't understand, and apparently `gc_types = true` is what tells the test infra not to use Winch. Signed-off-by: Joel Dice trap if async function finishes without calling `task.return` Signed-off-by: Joel Dice update wit-bindgen and fix rebase damage Signed-off-by: Joel Dice call post-return function if any for async->sync fused calls Signed-off-by: Joel Dice --- Cargo.lock | 130 + Cargo.toml | 8 +- benches/call.rs | 5 +- crates/component-macro/Cargo.toml | 4 +- crates/component-macro/src/bindgen.rs | 48 +- crates/component-macro/tests/codegen.rs | 8 + crates/component-macro/tests/expanded.rs | 8 + crates/component-macro/tests/expanded/char.rs | 29 +- .../tests/expanded/char_async.rs | 25 +- .../tests/expanded/char_concurrent.rs | 489 +++ .../tests/expanded/char_tracing_async.rs | 25 +- .../tests/expanded/conventions.rs | 79 +- .../tests/expanded/conventions_async.rs | 45 +- .../tests/expanded/conventions_concurrent.rs | 1361 +++++++ .../expanded/conventions_tracing_async.rs | 45 +- .../tests/expanded/dead-code.rs | 31 +- .../tests/expanded/dead-code_async.rs | 33 +- .../tests/expanded/dead-code_concurrent.rs | 341 ++ .../tests/expanded/dead-code_tracing_async.rs | 33 +- .../tests/expanded/direct-import.rs | 19 +- .../tests/expanded/direct-import_async.rs | 21 +- .../expanded/direct-import_concurrent.rs | 257 ++ .../expanded/direct-import_tracing_async.rs | 21 +- .../component-macro/tests/expanded/empty.rs | 7 +- .../tests/expanded/empty_async.rs | 9 +- .../tests/expanded/empty_concurrent.rs | 162 + .../tests/expanded/empty_tracing_async.rs | 9 +- .../component-macro/tests/expanded/flags.rs | 54 +- .../tests/expanded/flags_async.rs | 35 +- .../tests/expanded/flags_concurrent.rs | 1188 ++++++ .../tests/expanded/flags_tracing_async.rs | 35 +- .../component-macro/tests/expanded/floats.rs | 39 +- .../tests/expanded/floats_async.rs | 29 +- .../tests/expanded/floats_concurrent.rs | 637 +++ .../tests/expanded/floats_tracing_async.rs | 29 +- .../tests/expanded/function-new.rs | 12 +- .../tests/expanded/function-new_async.rs | 11 +- .../tests/expanded/function-new_concurrent.rs | 187 + .../expanded/function-new_tracing_async.rs | 11 +- .../tests/expanded/host-world.rs | 19 +- .../tests/expanded/host-world_async.rs | 21 +- .../tests/expanded/host-world_concurrent.rs | 257 ++ .../expanded/host-world_tracing_async.rs | 21 +- .../tests/expanded/integers.rs | 109 +- .../tests/expanded/integers_async.rs | 57 +- .../tests/expanded/integers_concurrent.rs | 1788 +++++++++ .../tests/expanded/integers_tracing_async.rs | 57 +- .../component-macro/tests/expanded/lists.rs | 164 +- .../tests/expanded/lists_async.rs | 79 +- .../tests/expanded/lists_concurrent.rs | 3431 +++++++++++++++++ .../tests/expanded/lists_tracing_async.rs | 79 +- .../tests/expanded/many-arguments.rs | 29 +- .../tests/expanded/many-arguments_async.rs | 25 +- .../expanded/many-arguments_concurrent.rs | 827 ++++ .../expanded/many-arguments_tracing_async.rs | 25 +- .../tests/expanded/multi-return.rs | 44 +- .../tests/expanded/multi-return_async.rs | 31 +- .../tests/expanded/multi-return_concurrent.rs | 704 ++++ .../expanded/multi-return_tracing_async.rs | 31 +- .../tests/expanded/multiversion.rs | 41 +- .../tests/expanded/multiversion_async.rs | 37 +- .../tests/expanded/multiversion_concurrent.rs | 619 +++ .../expanded/multiversion_tracing_async.rs | 37 +- .../component-macro/tests/expanded/path1.rs | 19 +- .../tests/expanded/path1_async.rs | 21 +- .../tests/expanded/path1_concurrent.rs | 220 ++ .../tests/expanded/path1_tracing_async.rs | 21 +- .../component-macro/tests/expanded/path2.rs | 19 +- .../tests/expanded/path2_async.rs | 21 +- .../tests/expanded/path2_concurrent.rs | 220 ++ .../tests/expanded/path2_tracing_async.rs | 21 +- .../component-macro/tests/expanded/records.rs | 74 +- .../tests/expanded/records_async.rs | 43 +- .../tests/expanded/records_concurrent.rs | 1555 ++++++++ .../tests/expanded/records_tracing_async.rs | 43 +- .../component-macro/tests/expanded/rename.rs | 31 +- .../tests/expanded/rename_async.rs | 33 +- .../tests/expanded/rename_concurrent.rs | 329 ++ .../tests/expanded/rename_tracing_async.rs | 33 +- .../tests/expanded/resources-export.rs | 63 +- .../tests/expanded/resources-export_async.rs | 41 +- .../expanded/resources-export_concurrent.rs | 935 +++++ .../resources-export_tracing_async.rs | 41 +- .../tests/expanded/resources-import.rs | 115 +- .../tests/expanded/resources-import_async.rs | 111 +- .../expanded/resources-import_concurrent.rs | 2383 ++++++++++++ .../resources-import_tracing_async.rs | 111 +- .../tests/expanded/share-types.rs | 33 +- .../tests/expanded/share-types_async.rs | 35 +- .../tests/expanded/share-types_concurrent.rs | 496 +++ .../expanded/share-types_tracing_async.rs | 35 +- .../tests/expanded/simple-functions.rs | 49 +- .../tests/expanded/simple-functions_async.rs | 33 +- .../expanded/simple-functions_concurrent.rs | 805 ++++ .../simple-functions_tracing_async.rs | 33 +- .../tests/expanded/simple-lists.rs | 39 +- .../tests/expanded/simple-lists_async.rs | 29 +- .../tests/expanded/simple-lists_concurrent.rs | 770 ++++ .../expanded/simple-lists_tracing_async.rs | 29 +- .../tests/expanded/simple-wasi.rs | 31 +- .../tests/expanded/simple-wasi_async.rs | 33 +- .../tests/expanded/simple-wasi_concurrent.rs | 437 +++ .../expanded/simple-wasi_tracing_async.rs | 33 +- .../tests/expanded/small-anonymous.rs | 24 +- .../tests/expanded/small-anonymous_async.rs | 23 +- .../expanded/small-anonymous_concurrent.rs | 532 +++ .../expanded/small-anonymous_tracing_async.rs | 23 +- .../tests/expanded/smoke-default.rs | 12 +- .../tests/expanded/smoke-default_async.rs | 11 +- .../expanded/smoke-default_concurrent.rs | 187 + .../expanded/smoke-default_tracing_async.rs | 11 +- .../tests/expanded/smoke-export.rs | 12 +- .../tests/expanded/smoke-export_async.rs | 11 +- .../tests/expanded/smoke-export_concurrent.rs | 268 ++ .../expanded/smoke-export_tracing_async.rs | 11 +- .../component-macro/tests/expanded/smoke.rs | 16 +- .../tests/expanded/smoke_async.rs | 21 +- .../tests/expanded/smoke_concurrent.rs | 271 ++ .../tests/expanded/smoke_tracing_async.rs | 21 +- .../component-macro/tests/expanded/strings.rs | 34 +- .../tests/expanded/strings_async.rs | 27 +- .../tests/expanded/strings_concurrent.rs | 592 +++ .../tests/expanded/strings_tracing_async.rs | 27 +- .../tests/expanded/unstable-features.rs | 37 +- .../tests/expanded/unstable-features_async.rs | 39 +- .../expanded/unstable-features_concurrent.rs | 687 ++++ .../unstable-features_tracing_async.rs | 39 +- .../tests/expanded/unversioned-foo.rs | 19 +- .../tests/expanded/unversioned-foo_async.rs | 21 +- .../expanded/unversioned-foo_concurrent.rs | 303 ++ .../expanded/unversioned-foo_tracing_async.rs | 21 +- .../tests/expanded/use-paths.rs | 52 +- .../tests/expanded/use-paths_async.rs | 57 +- .../tests/expanded/use-paths_concurrent.rs | 604 +++ .../tests/expanded/use-paths_tracing_async.rs | 57 +- .../tests/expanded/variants.rs | 129 +- .../tests/expanded/variants_async.rs | 65 +- .../tests/expanded/variants_concurrent.rs | 3067 +++++++++++++++ .../tests/expanded/variants_tracing_async.rs | 65 +- crates/component-macro/tests/expanded/wat.rs | 7 +- .../tests/expanded/wat_async.rs | 9 +- .../tests/expanded/wat_concurrent.rs | 268 ++ .../tests/expanded/wat_tracing_async.rs | 9 +- .../tests/expanded/worlds-with-types.rs | 24 +- .../tests/expanded/worlds-with-types_async.rs | 23 +- .../expanded/worlds-with-types_concurrent.rs | 277 ++ .../worlds-with-types_tracing_async.rs | 23 +- crates/cranelift/Cargo.toml | 1 + crates/cranelift/src/compiler/component.rs | 1091 +++++- crates/environ/examples/factc.rs | 4 + crates/environ/src/component.rs | 4 + crates/environ/src/component/dfg.rs | 239 +- crates/environ/src/component/info.rs | 301 +- crates/environ/src/component/translate.rs | 284 +- .../environ/src/component/translate/adapt.rs | 24 +- .../environ/src/component/translate/inline.rs | 326 ++ crates/environ/src/component/types.rs | 102 +- crates/environ/src/component/types_builder.rs | 181 +- .../src/component/types_builder/resources.rs | 5 + .../src/component/vmcomponent_offsets.rs | 269 +- crates/environ/src/fact.rs | 208 +- crates/environ/src/fact/signature.rs | 99 +- crates/environ/src/fact/trampoline.rs | 620 ++- crates/environ/src/trap_encoding.rs | 7 +- .../fuzzing/src/generators/component_types.rs | 31 +- crates/fuzzing/src/generators/config.rs | 3 + crates/fuzzing/src/generators/module.rs | 2 + 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 | 1162 ++++++ .../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 | 99 + crates/misc/component-test-util/src/lib.rs | 25 +- crates/test-programs/Cargo.toml | 3 + crates/test-programs/artifacts/Cargo.toml | 1 + crates/test-programs/artifacts/build.rs | 18 +- .../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_post_return_callee.rs | 78 + .../src/bin/async_post_return_caller.rs | 35 + .../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 + 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 | 2192 +++++++++++ .../concurrent/futures_and_streams.rs | 2070 ++++++++++ .../component/concurrent/ready_chunks.rs | 59 + .../src/runtime/component/concurrent/table.rs | 316 ++ crates/wasmtime/src/runtime/component/func.rs | 504 ++- .../src/runtime/component/func/host.rs | 397 +- .../src/runtime/component/func/options.rs | 18 +- .../src/runtime/component/func/typed.rs | 247 +- .../src/runtime/component/instance.rs | 77 +- .../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 +- .../wasmtime/src/runtime/externals/table.rs | 36 +- crates/wasmtime/src/runtime/func.rs | 89 +- crates/wasmtime/src/runtime/func/typed.rs | 20 +- crates/wasmtime/src/runtime/instance.rs | 15 +- crates/wasmtime/src/runtime/linker.rs | 68 +- crates/wasmtime/src/runtime/memory.rs | 32 +- crates/wasmtime/src/runtime/store.rs | 226 +- crates/wasmtime/src/runtime/vm/component.rs | 524 ++- .../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 +- .../wasmtime/src/runtime/vm/traphandlers.rs | 73 +- crates/wasmtime/src/runtime/wave/component.rs | 10 +- crates/wast-util/src/lib.rs | 1 + crates/wast/Cargo.toml | 1 + crates/wast/src/component.rs | 3 + crates/wast/src/spectest.rs | 3 + crates/wit-bindgen/Cargo.toml | 1 + crates/wit-bindgen/src/lib.rs | 913 ++++- crates/wit-bindgen/src/rust.rs | 41 +- crates/wit-bindgen/src/types.rs | 13 +- tests/all/component_model/bindgen.rs | 174 +- tests/all/component_model/call_hook.rs | 7 +- tests/all/component_model/dynamic.rs | 2 +- tests/all/component_model/func.rs | 664 +++- tests/all/component_model/import.rs | 455 ++- tests/all/pooling_allocator.rs | 2 +- .../component-model-async/error-context.wast | 35 + .../component-model-async/fused.wast | 55 + .../component-model-async/futures.wast | 90 + .../component-model-async/lift.wast | 26 + .../component-model-async/lower.wast | 13 + .../component-model-async/streams.wast | 90 + .../component-model-async/task-builtins.wast | 60 + 250 files changed, 47045 insertions(+), 2388 deletions(-) create mode 100644 crates/component-macro/tests/expanded/char_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/conventions_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/dead-code_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/direct-import_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/empty_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/flags_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/floats_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/function-new_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/host-world_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/integers_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/lists_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/many-arguments_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/multi-return_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/multiversion_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/path1_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/path2_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/records_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/rename_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/resources-export_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/resources-import_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/share-types_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/simple-functions_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/simple-lists_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/simple-wasi_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/small-anonymous_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/smoke-default_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/smoke-export_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/smoke_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/strings_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/unstable-features_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/use-paths_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/variants_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/wat_concurrent.rs create mode 100644 crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs 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_post_return_callee.rs create mode 100644 crates/test-programs/src/bin/async_post_return_caller.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 create mode 100644 tests/misc_testsuite/component-model-async/error-context.wast create mode 100644 tests/misc_testsuite/component-model-async/fused.wast create mode 100644 tests/misc_testsuite/component-model-async/futures.wast create mode 100644 tests/misc_testsuite/component-model-async/lift.wast create mode 100644 tests/misc_testsuite/component-model-async/lower.wast create mode 100644 tests/misc_testsuite/component-model-async/streams.wast create mode 100644 tests/misc_testsuite/component-model-async/task-builtins.wast diff --git a/Cargo.lock b/Cargo.lock index 3ce594f53476..21c8283ab7f5 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" @@ -611,6 +620,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" @@ -1346,6 +1373,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" @@ -1769,6 +1802,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" @@ -2407,6 +2454,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.7.0", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2589,6 +2646,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" @@ -2908,6 +2974,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.7.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2981,6 +3060,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" @@ -3220,14 +3309,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]] @@ -3236,6 +3328,7 @@ version = "0.0.0" dependencies = [ "cargo_metadata", "heck 0.5.0", + "wasmparser", "wasmtime", "wit-component", ] @@ -3552,6 +3645,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" @@ -3721,6 +3820,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" @@ -3797,6 +3905,27 @@ version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +[[package]] +name = "wasm-compose" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d97ce79500f03efc3a5a173f0795811ad55775634f4a5be102c8bd23045349" +dependencies = [ + "anyhow", + "heck 0.4.1", + "im-rc", + "indexmap 2.7.0", + "log", + "petgraph", + "serde", + "serde_derive", + "serde_yaml", + "smallvec", + "wasm-encoder", + "wasmparser", + "wat", +] + [[package]] name = "wasm-encoder" version = "0.223.0" @@ -3962,6 +4091,7 @@ dependencies = [ "cfg-if", "encoding_rs", "env_logger 0.11.5", + "futures", "fxprof-processed-profile", "gimli", "hashbrown 0.15.2", diff --git a/Cargo.toml b/Cargo.toml index 75b2ce38710e..ab34ef76d7dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ rustix = { workspace = true, features = ["mm", "param", "process"] } [dev-dependencies] # depend again on wasmtime to activate its default features for tests -wasmtime = { workspace = true, features = ['default', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys', 'signals-based-traps'] } +wasmtime = { workspace = true, features = ['default', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys', 'signals-based-traps', 'component-model-async'] } env_logger = { workspace = true } log = { workspace = true } filecheck = { workspace = true } @@ -100,7 +100,7 @@ async-trait = { workspace = true } trait-variant = { workspace = true } wat = { workspace = true } rayon = "1.5.0" -wasmtime-wast = { workspace = true, features = ['component-model'] } +wasmtime-wast = { workspace = true, features = ['component-model', 'component-model-async'] } wasmtime-component-util = { workspace = true } component-macro-test = { path = "crates/misc/component-macro-test" } component-test-util = { workspace = true } @@ -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", @@ -241,6 +242,7 @@ wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", ve wasmtime-slab = { path = "crates/slab", version = "=30.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 = "=30.0.0", default-features = false } wiggle-macro = { path = "crates/wiggle/macro", version = "=30.0.0" } wiggle-generate = { path = "crates/wiggle/generate", version = "=30.0.0" } @@ -294,6 +296,7 @@ io-extras = "0.18.1" rustix = "0.38.43" # wit-bindgen: wit-bindgen = { version = "0.37.0", default-features = false } +wit-bindgen-rt = { version = "0.37.0", default-features = false } wit-bindgen-rust-macro = { version = "0.37.0", default-features = false } # wasm-tools family: @@ -307,6 +310,7 @@ wasm-mutate = "0.223.0" wit-parser = "0.223.0" wit-component = "0.223.0" wasm-wave = "0.223.0" +wasm-compose = "0.223.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- diff --git a/benches/call.rs b/benches/call.rs index 8e7d95aa8ffb..610b57452430 100644 --- a/benches/call.rs +++ b/benches/call.rs @@ -135,7 +135,7 @@ fn bench_host_to_wasm( typed_results: Results, ) where Params: WasmParams + ToVals + Copy, - Results: WasmResults + ToVals + Copy + PartialEq + Debug, + Results: WasmResults + ToVals + Copy + PartialEq + Debug + Sync + 'static, { // Benchmark the "typed" version, which should be faster than the versions // below. @@ -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..0d429aa080e2 100644 --- a/crates/component-macro/Cargo.toml +++ b/crates/component-macro/Cargo.toml @@ -29,7 +29,8 @@ wasmtime-wit-bindgen = { workspace = true } wit-parser = { workspace = true } [dev-dependencies] -wasmtime = { path = '../wasmtime', features = ['component-model'] } +wasmtime = { path = '../wasmtime', features = ['component-model', 'component-model-async'] } +wasmtime-wit-bindgen = { workspace = true, features = ['component-model-async'] } component-macro-test-helpers = { path = 'test-helpers' } tracing = { workspace = true } # For use with the custom attributes test @@ -41,3 +42,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/codegen.rs b/crates/component-macro/tests/codegen.rs index 73d61fd99539..0846ff8c7cf1 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -12,6 +12,14 @@ macro_rules! gentest { async: true, }); } + mod concurrent { + wasmtime::component::bindgen!({ + path: $path, + async: true, + concurrent_imports: true, + concurrent_exports: true, + }); + } mod tracing { wasmtime::component::bindgen!({ path: $path, diff --git a/crates/component-macro/tests/expanded.rs b/crates/component-macro/tests/expanded.rs index 29e338bc02f0..216e4cb47434 100644 --- a/crates/component-macro/tests/expanded.rs +++ b/crates/component-macro/tests/expanded.rs @@ -15,6 +15,14 @@ macro_rules! genexpand { stringify: true, }))?; + process_expanded($path, "_concurrent", wasmtime::component::bindgen!({ + path: $path, + async: true, + concurrent_imports: true, + concurrent_exports: true, + stringify: true, + }))?; + process_expanded($path, "_tracing_async", wasmtime::component::bindgen!({ path: $path, async: true, diff --git a/crates/component-macro/tests/expanded/char.rs b/crates/component-macro/tests/expanded/char.rs index 6ff749f08e7f..edbc44e5a635 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 730c6acb3213..ba02d7aff7b7 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_concurrent.rs b/crates/component-macro/tests/expanded/char_concurrent.rs new file mode 100644 index 000000000000..078960acf520 --- /dev/null +++ b/crates/component-macro/tests/expanded/char_concurrent.rs @@ -0,0 +1,489 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::chars::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::chars::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::chars::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::chars::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::chars::Host + 'static, + U: Send + foo::foo::chars::Host, + { + foo::foo::chars::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_chars(&self) -> &exports::foo::foo::chars::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod chars { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + /// A function that accepts a character + fn take_char( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: char, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + /// A function that returns a character + fn return_char( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> char + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/chars")?; + inst.func_wrap_concurrent( + "take-char", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (char,)| + { + let host = caller; + let r = ::take_char(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-char", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_char(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(char,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(char,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + /// A function that accepts a character + fn take_char( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: char, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::take_char(store, x) + } + /// A function that returns a character + fn return_char( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> char + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_char(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod chars { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + take_char: wasmtime::component::Func, + return_char: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + take_char: wasmtime::component::ComponentExportIndex, + return_char: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/chars") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/chars`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/chars") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/chars`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/chars` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let take_char = lookup("take-char")?; + let return_char = lookup("return-char")?; + Ok(GuestIndices { + take_char, + return_char, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let take_char = *_instance + .get_typed_func::<(char,), ()>(&mut store, &self.take_char)? + .func(); + let return_char = *_instance + .get_typed_func::< + (), + (char,), + >(&mut store, &self.return_char)? + .func(); + Ok(Guest { take_char, return_char }) + } + } + impl Guest { + /// A function that accepts a character + pub async fn call_take_char( + &self, + mut store: S, + arg0: char, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (char,), + (), + >::new_unchecked(self.take_char) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + /// A function that returns a character + pub async fn call_return_char( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (char,), + >::new_unchecked(self.return_char) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/char_tracing_async.rs b/crates/component-macro/tests/expanded/char_tracing_async.rs index aa1f926ea35f..9e7babae3067 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 b808807a8140..104e4e2af8a3 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 1e8f3bbd0693..95944236d2dc 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_concurrent.rs b/crates/component-macro/tests/expanded/conventions_concurrent.rs new file mode 100644 index 000000000000..db1e6f41b95b --- /dev/null +++ b/crates/component-macro/tests/expanded/conventions_concurrent.rs @@ -0,0 +1,1361 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::conventions::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::conventions::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::conventions::GuestIndices::new( + _component, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::conventions::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::conventions::Host + 'static, + U: Send + foo::foo::conventions::Host, + { + foo::foo::conventions::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_conventions(&self) -> &exports::foo::foo::conventions::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod conventions { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct LudicrousSpeed { + #[component(name = "how-fast-are-you-going")] + pub how_fast_are_you_going: u32, + #[component(name = "i-am-going-extremely-slow")] + pub i_am_going_extremely_slow: u64, + } + impl core::fmt::Debug for LudicrousSpeed { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LudicrousSpeed") + .field("how-fast-are-you-going", &self.how_fast_are_you_going) + .field( + "i-am-going-extremely-slow", + &self.i_am_going_extremely_slow, + ) + .finish() + } + } + const _: () = { + assert!( + 16 == < LudicrousSpeed as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 8 == < LudicrousSpeed as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub trait Host { + type Data; + fn kebab_case( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: LudicrousSpeed, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn function_with_dashes( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn function_with_no_weird_characters( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn apple( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn apple_pear( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn apple_pear_grape( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a0( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + /// Comment out identifiers that collide when mapped to snake_case, for now; see + /// https://github.com/WebAssembly/component-model/issues/118 + /// APPLE: func() + /// APPLE-pear-GRAPE: func() + /// apple-PEAR-grape: func() + fn is_xml( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn explicit( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn explicit_kebab( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + /// Identifiers with the same name as keywords are quoted. + fn bool( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/conventions")?; + inst.func_wrap_concurrent( + "kebab-case", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::kebab_case(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "foo", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (LudicrousSpeed,)| + { + let host = caller; + let r = ::foo(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "function-with-dashes", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::function_with_dashes(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "function-with-no-weird-characters", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::function_with_no_weird_characters( + host, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "apple", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::apple(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "apple-pear", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::apple_pear(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "apple-pear-grape", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::apple_pear_grape(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a0", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::a0(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "is-XML", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::is_xml(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "explicit", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::explicit(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "explicit-kebab", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::explicit_kebab(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bool", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::bool(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn kebab_case( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::kebab_case(store) + } + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: LudicrousSpeed, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::foo(store, x) + } + fn function_with_dashes( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::function_with_dashes(store) + } + fn function_with_no_weird_characters( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::function_with_no_weird_characters(store) + } + fn apple( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::apple(store) + } + fn apple_pear( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::apple_pear(store) + } + fn apple_pear_grape( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::apple_pear_grape(store) + } + fn a0( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a0(store) + } + /// Comment out identifiers that collide when mapped to snake_case, for now; see + /// https://github.com/WebAssembly/component-model/issues/118 + /// APPLE: func() + /// APPLE-pear-GRAPE: func() + /// apple-PEAR-grape: func() + fn is_xml( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::is_xml(store) + } + fn explicit( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::explicit(store) + } + fn explicit_kebab( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::explicit_kebab(store) + } + /// Identifiers with the same name as keywords are quoted. + fn bool( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bool(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod conventions { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct LudicrousSpeed { + #[component(name = "how-fast-are-you-going")] + pub how_fast_are_you_going: u32, + #[component(name = "i-am-going-extremely-slow")] + pub i_am_going_extremely_slow: u64, + } + impl core::fmt::Debug for LudicrousSpeed { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("LudicrousSpeed") + .field( + "how-fast-are-you-going", + &self.how_fast_are_you_going, + ) + .field( + "i-am-going-extremely-slow", + &self.i_am_going_extremely_slow, + ) + .finish() + } + } + const _: () = { + assert!( + 16 == < LudicrousSpeed as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 8 == < LudicrousSpeed as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub struct Guest { + kebab_case: wasmtime::component::Func, + foo: wasmtime::component::Func, + function_with_dashes: wasmtime::component::Func, + function_with_no_weird_characters: wasmtime::component::Func, + apple: wasmtime::component::Func, + apple_pear: wasmtime::component::Func, + apple_pear_grape: wasmtime::component::Func, + a0: wasmtime::component::Func, + is_xml: wasmtime::component::Func, + explicit: wasmtime::component::Func, + explicit_kebab: wasmtime::component::Func, + bool: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + kebab_case: wasmtime::component::ComponentExportIndex, + foo: wasmtime::component::ComponentExportIndex, + function_with_dashes: wasmtime::component::ComponentExportIndex, + function_with_no_weird_characters: wasmtime::component::ComponentExportIndex, + apple: wasmtime::component::ComponentExportIndex, + apple_pear: wasmtime::component::ComponentExportIndex, + apple_pear_grape: wasmtime::component::ComponentExportIndex, + a0: wasmtime::component::ComponentExportIndex, + is_xml: wasmtime::component::ComponentExportIndex, + explicit: wasmtime::component::ComponentExportIndex, + explicit_kebab: wasmtime::component::ComponentExportIndex, + bool: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/conventions") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/conventions`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/conventions") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/conventions`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/conventions` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let kebab_case = lookup("kebab-case")?; + let foo = lookup("foo")?; + let function_with_dashes = lookup("function-with-dashes")?; + let function_with_no_weird_characters = lookup( + "function-with-no-weird-characters", + )?; + let apple = lookup("apple")?; + let apple_pear = lookup("apple-pear")?; + let apple_pear_grape = lookup("apple-pear-grape")?; + let a0 = lookup("a0")?; + let is_xml = lookup("is-XML")?; + let explicit = lookup("explicit")?; + let explicit_kebab = lookup("explicit-kebab")?; + let bool = lookup("bool")?; + Ok(GuestIndices { + kebab_case, + foo, + function_with_dashes, + function_with_no_weird_characters, + apple, + apple_pear, + apple_pear_grape, + a0, + is_xml, + explicit, + explicit_kebab, + bool, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let kebab_case = *_instance + .get_typed_func::<(), ()>(&mut store, &self.kebab_case)? + .func(); + let foo = *_instance + .get_typed_func::< + (LudicrousSpeed,), + (), + >(&mut store, &self.foo)? + .func(); + let function_with_dashes = *_instance + .get_typed_func::< + (), + (), + >(&mut store, &self.function_with_dashes)? + .func(); + let function_with_no_weird_characters = *_instance + .get_typed_func::< + (), + (), + >(&mut store, &self.function_with_no_weird_characters)? + .func(); + let apple = *_instance + .get_typed_func::<(), ()>(&mut store, &self.apple)? + .func(); + let apple_pear = *_instance + .get_typed_func::<(), ()>(&mut store, &self.apple_pear)? + .func(); + let apple_pear_grape = *_instance + .get_typed_func::< + (), + (), + >(&mut store, &self.apple_pear_grape)? + .func(); + let a0 = *_instance + .get_typed_func::<(), ()>(&mut store, &self.a0)? + .func(); + let is_xml = *_instance + .get_typed_func::<(), ()>(&mut store, &self.is_xml)? + .func(); + let explicit = *_instance + .get_typed_func::<(), ()>(&mut store, &self.explicit)? + .func(); + let explicit_kebab = *_instance + .get_typed_func::<(), ()>(&mut store, &self.explicit_kebab)? + .func(); + let bool = *_instance + .get_typed_func::<(), ()>(&mut store, &self.bool)? + .func(); + Ok(Guest { + kebab_case, + foo, + function_with_dashes, + function_with_no_weird_characters, + apple, + apple_pear, + apple_pear_grape, + a0, + is_xml, + explicit, + explicit_kebab, + bool, + }) + } + } + impl Guest { + pub async fn call_kebab_case( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.kebab_case) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_foo( + &self, + mut store: S, + arg0: LudicrousSpeed, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (LudicrousSpeed,), + (), + >::new_unchecked(self.foo) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_function_with_dashes( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.function_with_dashes) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_function_with_no_weird_characters< + S: wasmtime::AsContextMut, + >( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.function_with_no_weird_characters) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_apple( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.apple) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_apple_pear( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.apple_pear) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_apple_pear_grape( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.apple_pear_grape) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_a0( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.a0) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + /// Comment out identifiers that collide when mapped to snake_case, for now; see + /// https://github.com/WebAssembly/component-model/issues/118 + /// APPLE: func() + /// APPLE-pear-GRAPE: func() + /// apple-PEAR-grape: func() + pub async fn call_is_xml( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.is_xml) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_explicit( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.explicit) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_explicit_kebab( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.explicit_kebab) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + /// Identifiers with the same name as keywords are quoted. + pub async fn call_bool( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.bool) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/conventions_tracing_async.rs b/crates/component-macro/tests/expanded/conventions_tracing_async.rs index 011ca5b9f146..56dc6d86fe0d 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 23f775721411..67f3e2b405de 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 7ba8b30a3908..d8e0de52d786 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_concurrent.rs b/crates/component-macro/tests/expanded/dead-code_concurrent.rs new file mode 100644 index 000000000000..9f0646a0f2c4 --- /dev/null +++ b/crates/component-macro/tests/expanded/dead-code_concurrent.rs @@ -0,0 +1,341 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `imports`. +/// +/// This structure is created through [`ImportsPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Imports`] as well. +pub struct ImportsPre { + instance_pre: wasmtime::component::InstancePre, + indices: ImportsIndices, +} +impl Clone for ImportsPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> ImportsPre<_T> { + /// Creates a new copy of `ImportsPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = ImportsIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Imports`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `imports`. +/// +/// This is an implementation detail of [`ImportsPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Imports`] as well. +#[derive(Clone)] +pub struct ImportsIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `imports`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Imports::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`ImportsPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`ImportsPre::instantiate_async`] to +/// create a [`Imports`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Imports::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`ImportsIndices::new_instance`] followed +/// by [`ImportsIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Imports {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl ImportsIndices { + /// Creates a new copy of `ImportsIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(ImportsIndices {}) + } + /// Creates a new instance of [`ImportsIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Imports`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Imports`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(ImportsIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Imports`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Imports {}) + } + } + impl Imports { + /// Convenience wrapper around [`ImportsPre::new`] and + /// [`ImportsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + ImportsPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`ImportsIndices::new_instance`] and + /// [`ImportsIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = ImportsIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + a::b::interface_with_live_type::Host + + a::b::interface_with_dead_type::Host + 'static, + U: Send + a::b::interface_with_live_type::Host + + a::b::interface_with_dead_type::Host, + { + a::b::interface_with_live_type::add_to_linker(linker, get)?; + a::b::interface_with_dead_type::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod a { + pub mod b { + #[allow(clippy::all)] + pub mod interface_with_live_type { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct LiveType { + #[component(name = "a")] + pub a: u32, + } + impl core::fmt::Debug for LiveType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LiveType").field("a", &self.a).finish() + } + } + const _: () = { + assert!(4 == < LiveType as wasmtime::component::ComponentType >::SIZE32); + assert!( + 4 == < LiveType as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub trait Host { + type Data; + fn f( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> LiveType + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("a:b/interface-with-live-type")?; + inst.func_wrap_concurrent( + "f", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(LiveType,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(LiveType,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn f( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> LiveType + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f(store) + } + } + } + #[allow(clippy::all)] + pub mod interface_with_dead_type { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("a:b/interface-with-dead-type")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} 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 b394499697c0..9c774ed63485 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..fc36862fca1f 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. /// @@ -97,10 +97,11 @@ pub trait FooImports { } pub trait FooImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: FooImports; } -impl FooImportsGetHost for F +impl FooImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: FooImports, @@ -162,7 +163,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) } @@ -175,9 +179,12 @@ const _: () = { let indices = FooIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> FooImportsGetHost<&'a mut T, T, Host: FooImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> FooImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut linker = linker.root(); linker diff --git a/crates/component-macro/tests/expanded/direct-import_async.rs b/crates/component-macro/tests/expanded/direct-import_async.rs index a0ab29ebc481..3ea92cfdbe8c 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) @@ -101,10 +98,11 @@ pub trait FooImports: Send { } pub trait FooImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: FooImports; } -impl FooImportsGetHost for F +impl FooImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: FooImports, @@ -168,7 +166,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 @@ -182,9 +180,12 @@ const _: () = { let indices = FooIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> FooImportsGetHost<&'a mut T, T, Host: FooImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> FooImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/direct-import_concurrent.rs b/crates/component-macro/tests/expanded/direct-import_concurrent.rs new file mode 100644 index 000000000000..eb4da17b894d --- /dev/null +++ b/crates/component-macro/tests/expanded/direct-import_concurrent.rs @@ -0,0 +1,257 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `foo`. +/// +/// This structure is created through [`FooPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Foo`] as well. +pub struct FooPre { + instance_pre: wasmtime::component::InstancePre, + indices: FooIndices, +} +impl Clone for FooPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Creates a new copy of `FooPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = FooIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Foo`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `foo`. +/// +/// This is an implementation detail of [`FooPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Foo`] as well. +#[derive(Clone)] +pub struct FooIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `foo`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Foo::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`FooPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`FooPre::instantiate_async`] to +/// create a [`Foo`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Foo::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`FooIndices::new_instance`] followed +/// by [`FooIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Foo {} +pub trait FooImports { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; +} +pub trait FooImportsGetHost< + T, + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: FooImports; +} +impl FooImportsGetHost for F +where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: FooImports, +{ + type Host = O; +} +impl<_T: FooImports> FooImports for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as FooImports>::foo(store) + } +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl FooIndices { + /// Creates a new copy of `FooIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(FooIndices {}) + } + /// Creates a new instance of [`FooIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Foo`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Foo`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(FooIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Foo`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Foo {}) + } + } + impl Foo { + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`FooIndices::new_instance`] and + /// [`FooIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = FooIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> FooImportsGetHost<&'a mut T, T, Host: FooImports>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut linker = linker.root(); + linker + .func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + FooImports + 'static, + U: Send + FooImports, + { + Self::add_to_linker_imports_get_host(linker, get)?; + Ok(()) + } + } +}; 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..5c9c95aca46e 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) @@ -101,10 +98,11 @@ pub trait FooImports: Send { } pub trait FooImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: FooImports; } -impl FooImportsGetHost for F +impl FooImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: FooImports, @@ -168,7 +166,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 @@ -182,9 +180,12 @@ const _: () = { let indices = FooIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> FooImportsGetHost<&'a mut T, T, Host: FooImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> FooImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, 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_concurrent.rs b/crates/component-macro/tests/expanded/empty_concurrent.rs new file mode 100644 index 000000000000..264f37601052 --- /dev/null +++ b/crates/component-macro/tests/expanded/empty_concurrent.rs @@ -0,0 +1,162 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `empty`. +/// +/// This structure is created through [`EmptyPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Empty`] as well. +pub struct EmptyPre { + instance_pre: wasmtime::component::InstancePre, + indices: EmptyIndices, +} +impl Clone for EmptyPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> EmptyPre<_T> { + /// Creates a new copy of `EmptyPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = EmptyIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Empty`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `empty`. +/// +/// This is an implementation detail of [`EmptyPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Empty`] as well. +#[derive(Clone)] +pub struct EmptyIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `empty`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Empty::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`EmptyPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`EmptyPre::instantiate_async`] to +/// create a [`Empty`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Empty::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`EmptyIndices::new_instance`] followed +/// by [`EmptyIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Empty {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl EmptyIndices { + /// Creates a new copy of `EmptyIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(EmptyIndices {}) + } + /// Creates a new instance of [`EmptyIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Empty`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Empty`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(EmptyIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Empty`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Empty {}) + } + } + impl Empty { + /// Convenience wrapper around [`EmptyPre::new`] and + /// [`EmptyPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + EmptyPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`EmptyIndices::new_instance`] and + /// [`EmptyIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = EmptyIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + } +}; 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 456c9a239905..7919729ddab2 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 c872a542d0ce..ddd7bf9139bd 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_concurrent.rs b/crates/component-macro/tests/expanded/flags_concurrent.rs new file mode 100644 index 000000000000..1620d6700355 --- /dev/null +++ b/crates/component-macro/tests/expanded/flags_concurrent.rs @@ -0,0 +1,1188 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-flags`. +/// +/// This structure is created through [`TheFlagsPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheFlags`] as well. +pub struct TheFlagsPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheFlagsIndices, +} +impl Clone for TheFlagsPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheFlagsPre<_T> { + /// Creates a new copy of `TheFlagsPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheFlagsIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheFlags`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-flags`. +/// +/// This is an implementation detail of [`TheFlagsPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheFlags`] as well. +#[derive(Clone)] +pub struct TheFlagsIndices { + interface0: exports::foo::foo::flegs::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-flags`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheFlags::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheFlagsPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheFlagsPre::instantiate_async`] to +/// create a [`TheFlags`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheFlags::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheFlagsIndices::new_instance`] followed +/// by [`TheFlagsIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheFlags { + interface0: exports::foo::foo::flegs::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheFlagsIndices { + /// Creates a new copy of `TheFlagsIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::flegs::GuestIndices::new(_component)?; + Ok(TheFlagsIndices { interface0 }) + } + /// Creates a new instance of [`TheFlagsIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheFlags`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheFlags`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::flegs::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheFlagsIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheFlags`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheFlags { interface0 }) + } + } + impl TheFlags { + /// Convenience wrapper around [`TheFlagsPre::new`] and + /// [`TheFlagsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheFlagsPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheFlagsIndices::new_instance`] and + /// [`TheFlagsIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheFlagsIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::flegs::Host + 'static, + U: Send + foo::foo::flegs::Host, + { + foo::foo::flegs::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_flegs(&self) -> &exports::foo::foo::flegs::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod flegs { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + wasmtime::component::flags!(Flag1 { #[component(name = "b0")] const B0; }); + const _: () = { + assert!(1 == < Flag1 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Flag1 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag2 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; } + ); + const _: () = { + assert!(1 == < Flag2 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Flag2 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag4 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = "b3")] + const B3; } + ); + const _: () = { + assert!(1 == < Flag4 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Flag4 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag8 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = "b3")] + const B3; #[component(name = "b4")] const B4; #[component(name = "b5")] + const B5; #[component(name = "b6")] const B6; #[component(name = "b7")] + const B7; } + ); + const _: () = { + assert!(1 == < Flag8 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Flag8 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag16 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = "b3")] + const B3; #[component(name = "b4")] const B4; #[component(name = "b5")] + const B5; #[component(name = "b6")] const B6; #[component(name = "b7")] + const B7; #[component(name = "b8")] const B8; #[component(name = "b9")] + const B9; #[component(name = "b10")] const B10; #[component(name = + "b11")] const B11; #[component(name = "b12")] const B12; #[component(name + = "b13")] const B13; #[component(name = "b14")] const B14; + #[component(name = "b15")] const B15; } + ); + const _: () = { + assert!(2 == < Flag16 as wasmtime::component::ComponentType >::SIZE32); + assert!(2 == < Flag16 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag32 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = "b3")] + const B3; #[component(name = "b4")] const B4; #[component(name = "b5")] + const B5; #[component(name = "b6")] const B6; #[component(name = "b7")] + const B7; #[component(name = "b8")] const B8; #[component(name = "b9")] + const B9; #[component(name = "b10")] const B10; #[component(name = + "b11")] const B11; #[component(name = "b12")] const B12; #[component(name + = "b13")] const B13; #[component(name = "b14")] const B14; + #[component(name = "b15")] const B15; #[component(name = "b16")] const + B16; #[component(name = "b17")] const B17; #[component(name = "b18")] + const B18; #[component(name = "b19")] const B19; #[component(name = + "b20")] const B20; #[component(name = "b21")] const B21; #[component(name + = "b22")] const B22; #[component(name = "b23")] const B23; + #[component(name = "b24")] const B24; #[component(name = "b25")] const + B25; #[component(name = "b26")] const B26; #[component(name = "b27")] + const B27; #[component(name = "b28")] const B28; #[component(name = + "b29")] const B29; #[component(name = "b30")] const B30; #[component(name + = "b31")] const B31; } + ); + const _: () = { + assert!(4 == < Flag32 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Flag32 as wasmtime::component::ComponentType >::ALIGN32); + }; + wasmtime::component::flags!( + Flag64 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = "b3")] + const B3; #[component(name = "b4")] const B4; #[component(name = "b5")] + const B5; #[component(name = "b6")] const B6; #[component(name = "b7")] + const B7; #[component(name = "b8")] const B8; #[component(name = "b9")] + const B9; #[component(name = "b10")] const B10; #[component(name = + "b11")] const B11; #[component(name = "b12")] const B12; #[component(name + = "b13")] const B13; #[component(name = "b14")] const B14; + #[component(name = "b15")] const B15; #[component(name = "b16")] const + B16; #[component(name = "b17")] const B17; #[component(name = "b18")] + const B18; #[component(name = "b19")] const B19; #[component(name = + "b20")] const B20; #[component(name = "b21")] const B21; #[component(name + = "b22")] const B22; #[component(name = "b23")] const B23; + #[component(name = "b24")] const B24; #[component(name = "b25")] const + B25; #[component(name = "b26")] const B26; #[component(name = "b27")] + const B27; #[component(name = "b28")] const B28; #[component(name = + "b29")] const B29; #[component(name = "b30")] const B30; #[component(name + = "b31")] const B31; #[component(name = "b32")] const B32; + #[component(name = "b33")] const B33; #[component(name = "b34")] const + B34; #[component(name = "b35")] const B35; #[component(name = "b36")] + const B36; #[component(name = "b37")] const B37; #[component(name = + "b38")] const B38; #[component(name = "b39")] const B39; #[component(name + = "b40")] const B40; #[component(name = "b41")] const B41; + #[component(name = "b42")] const B42; #[component(name = "b43")] const + B43; #[component(name = "b44")] const B44; #[component(name = "b45")] + const B45; #[component(name = "b46")] const B46; #[component(name = + "b47")] const B47; #[component(name = "b48")] const B48; #[component(name + = "b49")] const B49; #[component(name = "b50")] const B50; + #[component(name = "b51")] const B51; #[component(name = "b52")] const + B52; #[component(name = "b53")] const B53; #[component(name = "b54")] + const B54; #[component(name = "b55")] const B55; #[component(name = + "b56")] const B56; #[component(name = "b57")] const B57; #[component(name + = "b58")] const B58; #[component(name = "b59")] const B59; + #[component(name = "b60")] const B60; #[component(name = "b61")] const + B61; #[component(name = "b62")] const B62; #[component(name = "b63")] + const B63; } + ); + const _: () = { + assert!(8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn roundtrip_flag1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag2, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag2 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag4, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag4 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag16( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag32( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn roundtrip_flag64( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/flegs")?; + inst.func_wrap_concurrent( + "roundtrip-flag1", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag1,)| + { + let host = caller; + let r = ::roundtrip_flag1(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag1,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag1,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag2", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag2,)| + { + let host = caller; + let r = ::roundtrip_flag2(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag2,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag2,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag4", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag4,)| + { + let host = caller; + let r = ::roundtrip_flag4(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag4,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag4,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag8", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag8,)| + { + let host = caller; + let r = ::roundtrip_flag8(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag8,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag8,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag16", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag16,)| + { + let host = caller; + let r = ::roundtrip_flag16(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag16,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag16,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag32", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag32,)| + { + let host = caller; + let r = ::roundtrip_flag32(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "roundtrip-flag64", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Flag64,)| + { + let host = caller; + let r = ::roundtrip_flag64(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag64,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Flag64,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn roundtrip_flag1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag1(store, x) + } + fn roundtrip_flag2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag2, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag2 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag2(store, x) + } + fn roundtrip_flag4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag4, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag4 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag4(store, x) + } + fn roundtrip_flag8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag8(store, x) + } + fn roundtrip_flag16( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag16(store, x) + } + fn roundtrip_flag32( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag32(store, x) + } + fn roundtrip_flag64( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Flag64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Flag64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::roundtrip_flag64(store, x) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod flegs { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + wasmtime::component::flags!( + Flag1 { #[component(name = "b0")] const B0; } + ); + const _: () = { + assert!( + 1 == < Flag1 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Flag1 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag2 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; } + ); + const _: () = { + assert!( + 1 == < Flag2 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Flag2 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag4 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = + "b3")] const B3; } + ); + const _: () = { + assert!( + 1 == < Flag4 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Flag4 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag8 { #[component(name = "b0")] const B0; #[component(name = "b1")] + const B1; #[component(name = "b2")] const B2; #[component(name = + "b3")] const B3; #[component(name = "b4")] const B4; #[component(name + = "b5")] const B5; #[component(name = "b6")] const B6; + #[component(name = "b7")] const B7; } + ); + const _: () = { + assert!( + 1 == < Flag8 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Flag8 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag16 { #[component(name = "b0")] const B0; #[component(name = + "b1")] const B1; #[component(name = "b2")] const B2; #[component(name + = "b3")] const B3; #[component(name = "b4")] const B4; + #[component(name = "b5")] const B5; #[component(name = "b6")] const + B6; #[component(name = "b7")] const B7; #[component(name = "b8")] + const B8; #[component(name = "b9")] const B9; #[component(name = + "b10")] const B10; #[component(name = "b11")] const B11; + #[component(name = "b12")] const B12; #[component(name = "b13")] + const B13; #[component(name = "b14")] const B14; #[component(name = + "b15")] const B15; } + ); + const _: () = { + assert!( + 2 == < Flag16 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 2 == < Flag16 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag32 { #[component(name = "b0")] const B0; #[component(name = + "b1")] const B1; #[component(name = "b2")] const B2; #[component(name + = "b3")] const B3; #[component(name = "b4")] const B4; + #[component(name = "b5")] const B5; #[component(name = "b6")] const + B6; #[component(name = "b7")] const B7; #[component(name = "b8")] + const B8; #[component(name = "b9")] const B9; #[component(name = + "b10")] const B10; #[component(name = "b11")] const B11; + #[component(name = "b12")] const B12; #[component(name = "b13")] + const B13; #[component(name = "b14")] const B14; #[component(name = + "b15")] const B15; #[component(name = "b16")] const B16; + #[component(name = "b17")] const B17; #[component(name = "b18")] + const B18; #[component(name = "b19")] const B19; #[component(name = + "b20")] const B20; #[component(name = "b21")] const B21; + #[component(name = "b22")] const B22; #[component(name = "b23")] + const B23; #[component(name = "b24")] const B24; #[component(name = + "b25")] const B25; #[component(name = "b26")] const B26; + #[component(name = "b27")] const B27; #[component(name = "b28")] + const B28; #[component(name = "b29")] const B29; #[component(name = + "b30")] const B30; #[component(name = "b31")] const B31; } + ); + const _: () = { + assert!( + 4 == < Flag32 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Flag32 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + wasmtime::component::flags!( + Flag64 { #[component(name = "b0")] const B0; #[component(name = + "b1")] const B1; #[component(name = "b2")] const B2; #[component(name + = "b3")] const B3; #[component(name = "b4")] const B4; + #[component(name = "b5")] const B5; #[component(name = "b6")] const + B6; #[component(name = "b7")] const B7; #[component(name = "b8")] + const B8; #[component(name = "b9")] const B9; #[component(name = + "b10")] const B10; #[component(name = "b11")] const B11; + #[component(name = "b12")] const B12; #[component(name = "b13")] + const B13; #[component(name = "b14")] const B14; #[component(name = + "b15")] const B15; #[component(name = "b16")] const B16; + #[component(name = "b17")] const B17; #[component(name = "b18")] + const B18; #[component(name = "b19")] const B19; #[component(name = + "b20")] const B20; #[component(name = "b21")] const B21; + #[component(name = "b22")] const B22; #[component(name = "b23")] + const B23; #[component(name = "b24")] const B24; #[component(name = + "b25")] const B25; #[component(name = "b26")] const B26; + #[component(name = "b27")] const B27; #[component(name = "b28")] + const B28; #[component(name = "b29")] const B29; #[component(name = + "b30")] const B30; #[component(name = "b31")] const B31; + #[component(name = "b32")] const B32; #[component(name = "b33")] + const B33; #[component(name = "b34")] const B34; #[component(name = + "b35")] const B35; #[component(name = "b36")] const B36; + #[component(name = "b37")] const B37; #[component(name = "b38")] + const B38; #[component(name = "b39")] const B39; #[component(name = + "b40")] const B40; #[component(name = "b41")] const B41; + #[component(name = "b42")] const B42; #[component(name = "b43")] + const B43; #[component(name = "b44")] const B44; #[component(name = + "b45")] const B45; #[component(name = "b46")] const B46; + #[component(name = "b47")] const B47; #[component(name = "b48")] + const B48; #[component(name = "b49")] const B49; #[component(name = + "b50")] const B50; #[component(name = "b51")] const B51; + #[component(name = "b52")] const B52; #[component(name = "b53")] + const B53; #[component(name = "b54")] const B54; #[component(name = + "b55")] const B55; #[component(name = "b56")] const B56; + #[component(name = "b57")] const B57; #[component(name = "b58")] + const B58; #[component(name = "b59")] const B59; #[component(name = + "b60")] const B60; #[component(name = "b61")] const B61; + #[component(name = "b62")] const B62; #[component(name = "b63")] + const B63; } + ); + const _: () = { + assert!( + 8 == < Flag64 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Flag64 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub struct Guest { + roundtrip_flag1: wasmtime::component::Func, + roundtrip_flag2: wasmtime::component::Func, + roundtrip_flag4: wasmtime::component::Func, + roundtrip_flag8: wasmtime::component::Func, + roundtrip_flag16: wasmtime::component::Func, + roundtrip_flag32: wasmtime::component::Func, + roundtrip_flag64: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + roundtrip_flag1: wasmtime::component::ComponentExportIndex, + roundtrip_flag2: wasmtime::component::ComponentExportIndex, + roundtrip_flag4: wasmtime::component::ComponentExportIndex, + roundtrip_flag8: wasmtime::component::ComponentExportIndex, + roundtrip_flag16: wasmtime::component::ComponentExportIndex, + roundtrip_flag32: wasmtime::component::ComponentExportIndex, + roundtrip_flag64: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/flegs") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/flegs`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/flegs") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/flegs`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/flegs` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let roundtrip_flag1 = lookup("roundtrip-flag1")?; + let roundtrip_flag2 = lookup("roundtrip-flag2")?; + let roundtrip_flag4 = lookup("roundtrip-flag4")?; + let roundtrip_flag8 = lookup("roundtrip-flag8")?; + let roundtrip_flag16 = lookup("roundtrip-flag16")?; + let roundtrip_flag32 = lookup("roundtrip-flag32")?; + let roundtrip_flag64 = lookup("roundtrip-flag64")?; + Ok(GuestIndices { + roundtrip_flag1, + roundtrip_flag2, + roundtrip_flag4, + roundtrip_flag8, + roundtrip_flag16, + roundtrip_flag32, + roundtrip_flag64, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let roundtrip_flag1 = *_instance + .get_typed_func::< + (Flag1,), + (Flag1,), + >(&mut store, &self.roundtrip_flag1)? + .func(); + let roundtrip_flag2 = *_instance + .get_typed_func::< + (Flag2,), + (Flag2,), + >(&mut store, &self.roundtrip_flag2)? + .func(); + let roundtrip_flag4 = *_instance + .get_typed_func::< + (Flag4,), + (Flag4,), + >(&mut store, &self.roundtrip_flag4)? + .func(); + let roundtrip_flag8 = *_instance + .get_typed_func::< + (Flag8,), + (Flag8,), + >(&mut store, &self.roundtrip_flag8)? + .func(); + let roundtrip_flag16 = *_instance + .get_typed_func::< + (Flag16,), + (Flag16,), + >(&mut store, &self.roundtrip_flag16)? + .func(); + let roundtrip_flag32 = *_instance + .get_typed_func::< + (Flag32,), + (Flag32,), + >(&mut store, &self.roundtrip_flag32)? + .func(); + let roundtrip_flag64 = *_instance + .get_typed_func::< + (Flag64,), + (Flag64,), + >(&mut store, &self.roundtrip_flag64)? + .func(); + Ok(Guest { + roundtrip_flag1, + roundtrip_flag2, + roundtrip_flag4, + roundtrip_flag8, + roundtrip_flag16, + roundtrip_flag32, + roundtrip_flag64, + }) + } + } + impl Guest { + pub async fn call_roundtrip_flag1( + &self, + mut store: S, + arg0: Flag1, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag1,), + (Flag1,), + >::new_unchecked(self.roundtrip_flag1) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag2( + &self, + mut store: S, + arg0: Flag2, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag2,), + (Flag2,), + >::new_unchecked(self.roundtrip_flag2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag4( + &self, + mut store: S, + arg0: Flag4, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag4,), + (Flag4,), + >::new_unchecked(self.roundtrip_flag4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag8( + &self, + mut store: S, + arg0: Flag8, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag8,), + (Flag8,), + >::new_unchecked(self.roundtrip_flag8) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag16( + &self, + mut store: S, + arg0: Flag16, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag16,), + (Flag16,), + >::new_unchecked(self.roundtrip_flag16) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag32( + &self, + mut store: S, + arg0: Flag32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag32,), + (Flag32,), + >::new_unchecked(self.roundtrip_flag32) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_roundtrip_flag64( + &self, + mut store: S, + arg0: Flag64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Flag64,), + (Flag64,), + >::new_unchecked(self.roundtrip_flag64) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/flags_tracing_async.rs b/crates/component-macro/tests/expanded/flags_tracing_async.rs index 01bfeb05ab6d..614a3b949032 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 89079f828361..247b1d813b89 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 4313d8ca2b95..cd894b77d8b9 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_concurrent.rs b/crates/component-macro/tests/expanded/floats_concurrent.rs new file mode 100644 index 000000000000..9254ce046993 --- /dev/null +++ b/crates/component-macro/tests/expanded/floats_concurrent.rs @@ -0,0 +1,637 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::floats::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::floats::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::floats::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::floats::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::floats::Host + 'static, + U: Send + foo::foo::floats::Host, + { + foo::foo::floats::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_floats(&self) -> &exports::foo::foo::floats::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod floats { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn f32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: f32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: f64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f32_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> f32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f64_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> f64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/floats")?; + inst.func_wrap_concurrent( + "f32-param", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (f32,)| { + let host = caller; + let r = ::f32_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f64-param", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (f64,)| { + let host = caller; + let r = ::f64_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f32-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f32_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(f32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(f32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f64-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f64_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(f64,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(f64,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn f32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: f32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f32_param(store, x) + } + fn f64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: f64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f64_param(store, x) + } + fn f32_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> f32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f32_result(store) + } + fn f64_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> f64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f64_result(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod floats { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + f32_param: wasmtime::component::Func, + f64_param: wasmtime::component::Func, + f32_result: wasmtime::component::Func, + f64_result: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + f32_param: wasmtime::component::ComponentExportIndex, + f64_param: wasmtime::component::ComponentExportIndex, + f32_result: wasmtime::component::ComponentExportIndex, + f64_result: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/floats") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/floats`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/floats") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/floats`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/floats` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let f32_param = lookup("f32-param")?; + let f64_param = lookup("f64-param")?; + let f32_result = lookup("f32-result")?; + let f64_result = lookup("f64-result")?; + Ok(GuestIndices { + f32_param, + f64_param, + f32_result, + f64_result, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let f32_param = *_instance + .get_typed_func::<(f32,), ()>(&mut store, &self.f32_param)? + .func(); + let f64_param = *_instance + .get_typed_func::<(f64,), ()>(&mut store, &self.f64_param)? + .func(); + let f32_result = *_instance + .get_typed_func::<(), (f32,)>(&mut store, &self.f32_result)? + .func(); + let f64_result = *_instance + .get_typed_func::<(), (f64,)>(&mut store, &self.f64_result)? + .func(); + Ok(Guest { + f32_param, + f64_param, + f32_result, + f64_result, + }) + } + } + impl Guest { + pub async fn call_f32_param( + &self, + mut store: S, + arg0: f32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (f32,), + (), + >::new_unchecked(self.f32_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_f64_param( + &self, + mut store: S, + arg0: f64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (f64,), + (), + >::new_unchecked(self.f64_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_f32_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (f32,), + >::new_unchecked(self.f32_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_f64_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (f64,), + >::new_unchecked(self.f64_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/floats_tracing_async.rs b/crates/component-macro/tests/expanded/floats_tracing_async.rs index 25194e376795..3f9e6d33f747 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_concurrent.rs b/crates/component-macro/tests/expanded/function-new_concurrent.rs new file mode 100644 index 000000000000..e85a5a95c480 --- /dev/null +++ b/crates/component-macro/tests/expanded/function-new_concurrent.rs @@ -0,0 +1,187 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `foo`. +/// +/// This structure is created through [`FooPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Foo`] as well. +pub struct FooPre { + instance_pre: wasmtime::component::InstancePre, + indices: FooIndices, +} +impl Clone for FooPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Creates a new copy of `FooPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = FooIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Foo`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `foo`. +/// +/// This is an implementation detail of [`FooPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Foo`] as well. +#[derive(Clone)] +pub struct FooIndices { + new: wasmtime::component::ComponentExportIndex, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `foo`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Foo::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`FooPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`FooPre::instantiate_async`] to +/// create a [`Foo`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Foo::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`FooIndices::new_instance`] followed +/// by [`FooIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Foo { + new: wasmtime::component::Func, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl FooIndices { + /// Creates a new copy of `FooIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let new = _component + .export_index(None, "new") + .ok_or_else(|| anyhow::anyhow!("no function export `new` found"))? + .1; + Ok(FooIndices { new }) + } + /// Creates a new instance of [`FooIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Foo`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Foo`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let new = _instance + .get_export(&mut store, None, "new") + .ok_or_else(|| anyhow::anyhow!("no function export `new` found"))?; + Ok(FooIndices { new }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Foo`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let new = *_instance.get_typed_func::<(), ()>(&mut store, &self.new)?.func(); + Ok(Foo { new }) + } + } + impl Foo { + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`FooIndices::new_instance`] and + /// [`FooIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = FooIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub async fn call_new( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.new) + }; + let promise = callee.call_concurrent(store.as_context_mut(), ()).await?; + Ok(promise) + } + } +}; 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..0ec2a32ee5ca 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. /// @@ -97,10 +97,11 @@ pub trait Host_Imports { } pub trait Host_ImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host_Imports; } -impl Host_ImportsGetHost for F +impl Host_ImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host_Imports, @@ -162,7 +163,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) } @@ -175,9 +179,12 @@ const _: () = { let indices = Host_Indices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> Host_ImportsGetHost<&'a mut T, T, Host: Host_Imports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> Host_ImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut linker = linker.root(); linker diff --git a/crates/component-macro/tests/expanded/host-world_async.rs b/crates/component-macro/tests/expanded/host-world_async.rs index d6dee9406e8c..d1c8e5cdcf62 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) @@ -101,10 +98,11 @@ pub trait Host_Imports: Send { } pub trait Host_ImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host_Imports; } -impl Host_ImportsGetHost for F +impl Host_ImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host_Imports, @@ -168,7 +166,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 @@ -182,9 +180,12 @@ const _: () = { let indices = Host_Indices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> Host_ImportsGetHost<&'a mut T, T, Host: Host_Imports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> Host_ImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/host-world_concurrent.rs b/crates/component-macro/tests/expanded/host-world_concurrent.rs new file mode 100644 index 000000000000..83b90871e6fd --- /dev/null +++ b/crates/component-macro/tests/expanded/host-world_concurrent.rs @@ -0,0 +1,257 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `host`. +/// +/// This structure is created through [`Host_Pre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Host_`] as well. +pub struct Host_Pre { + instance_pre: wasmtime::component::InstancePre, + indices: Host_Indices, +} +impl Clone for Host_Pre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +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. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = Host_Indices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Host_`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `host`. +/// +/// This is an implementation detail of [`Host_Pre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Host_`] as well. +#[derive(Clone)] +pub struct Host_Indices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `host`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Host_::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`Host_Pre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`Host_Pre::instantiate_async`] to +/// create a [`Host_`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Host_::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`Host_Indices::new_instance`] followed +/// by [`Host_Indices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Host_ {} +pub trait Host_Imports { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; +} +pub trait Host_ImportsGetHost< + T, + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host_Imports; +} +impl Host_ImportsGetHost for F +where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host_Imports, +{ + type Host = O; +} +impl<_T: Host_Imports> Host_Imports for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host_Imports>::foo(store) + } +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl Host_Indices { + /// Creates a new copy of `Host_Indices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(Host_Indices {}) + } + /// Creates a new instance of [`Host_Indices`] from an + /// instantiated component. + /// + /// This method of creating a [`Host_`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Host_`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Host_Indices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Host_`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Host_ {}) + } + } + impl Host_ { + /// Convenience wrapper around [`Host_Pre::new`] and + /// [`Host_Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + Host_Pre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`Host_Indices::new_instance`] and + /// [`Host_Indices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = Host_Indices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> Host_ImportsGetHost<&'a mut T, T, Host: Host_Imports>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut linker = linker.root(); + linker + .func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + Host_Imports + 'static, + U: Send + Host_Imports, + { + Self::add_to_linker_imports_get_host(linker, get)?; + Ok(()) + } + } +}; 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..8d166599b09f 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) @@ -101,10 +98,11 @@ pub trait Host_Imports: Send { } pub trait Host_ImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: Host_Imports; } -impl Host_ImportsGetHost for F +impl Host_ImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: Host_Imports, @@ -168,7 +166,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 @@ -182,9 +180,12 @@ const _: () = { let indices = Host_Indices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> Host_ImportsGetHost<&'a mut T, T, Host: Host_Imports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> Host_ImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, diff --git a/crates/component-macro/tests/expanded/integers.rs b/crates/component-macro/tests/expanded/integers.rs index fd4edcf6e1ab..6c95054c4a75 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 47045b1f6a92..fd73eabbd414 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_concurrent.rs b/crates/component-macro/tests/expanded/integers_concurrent.rs new file mode 100644 index 000000000000..b1be69822f8f --- /dev/null +++ b/crates/component-macro/tests/expanded/integers_concurrent.rs @@ -0,0 +1,1788 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::integers::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::integers::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::integers::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::integers::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::integers::Host + 'static, + U: Send + foo::foo::integers::Host, + { + foo::foo::integers::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_integers(&self) -> &exports::foo::foo::integers::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod integers { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn a1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a7( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn a9( + store: wasmtime::StoreContextMut<'_, Self::Data>, + p1: u8, + p2: i8, + p3: u16, + p4: i16, + p5: u32, + p6: i32, + p7: u64, + p8: i64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r7( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn r8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn pair_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (i64, u8) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/integers")?; + inst.func_wrap_concurrent( + "a1", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (u8,)| { + let host = caller; + let r = ::a1(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (i8,)| { + let host = caller; + let r = ::a2(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a3", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (u16,)| { + let host = caller; + let r = ::a3(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a4", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (i16,)| { + let host = caller; + let r = ::a4(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a5", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (u32,)| { + let host = caller; + let r = ::a5(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a6", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (i32,)| { + let host = caller; + let r = ::a6(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a7", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (u64,)| { + let host = caller; + let r = ::a7(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a8", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (i64,)| { + let host = caller; + let r = ::a8(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "a9", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + ): (u8, i8, u16, i16, u32, i32, u64, i64)| + { + let host = caller; + let r = ::a9( + host, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r1", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r1(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u8,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u8,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r2(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i8,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i8,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r3", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r3(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u16,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u16,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r4", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r4(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i16,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i16,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r5", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r5(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r6", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r6(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r7", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r7(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u64,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u64,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "r8", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::r8(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i64,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i64,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "pair-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::pair_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((i64, u8),)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((i64, u8),)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn a1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a1(store, x) + } + fn a2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i8, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a2(store, x) + } + fn a3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a3(store, x) + } + fn a4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i16, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a4(store, x) + } + fn a5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a5(store, x) + } + fn a6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a6(store, x) + } + fn a7( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: u64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a7(store, x) + } + fn a8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: i64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a8(store, x) + } + fn a9( + store: wasmtime::StoreContextMut<'_, Self::Data>, + p1: u8, + p2: i8, + p3: u16, + p4: i16, + p5: u32, + p6: i32, + p7: u64, + p8: i64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a9(store, p1, p2, p3, p4, p5, p6, p7, p8) + } + fn r1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r1(store) + } + fn r2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i8 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r2(store) + } + fn r3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r3(store) + } + fn r4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i16 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r4(store) + } + fn r5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r5(store) + } + fn r6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r6(store) + } + fn r7( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r7(store) + } + fn r8( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i64 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::r8(store) + } + fn pair_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (i64, u8) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::pair_ret(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod integers { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + a1: wasmtime::component::Func, + a2: wasmtime::component::Func, + a3: wasmtime::component::Func, + a4: wasmtime::component::Func, + a5: wasmtime::component::Func, + a6: wasmtime::component::Func, + a7: wasmtime::component::Func, + a8: wasmtime::component::Func, + a9: wasmtime::component::Func, + r1: wasmtime::component::Func, + r2: wasmtime::component::Func, + r3: wasmtime::component::Func, + r4: wasmtime::component::Func, + r5: wasmtime::component::Func, + r6: wasmtime::component::Func, + r7: wasmtime::component::Func, + r8: wasmtime::component::Func, + pair_ret: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + a1: wasmtime::component::ComponentExportIndex, + a2: wasmtime::component::ComponentExportIndex, + a3: wasmtime::component::ComponentExportIndex, + a4: wasmtime::component::ComponentExportIndex, + a5: wasmtime::component::ComponentExportIndex, + a6: wasmtime::component::ComponentExportIndex, + a7: wasmtime::component::ComponentExportIndex, + a8: wasmtime::component::ComponentExportIndex, + a9: wasmtime::component::ComponentExportIndex, + r1: wasmtime::component::ComponentExportIndex, + r2: wasmtime::component::ComponentExportIndex, + r3: wasmtime::component::ComponentExportIndex, + r4: wasmtime::component::ComponentExportIndex, + r5: wasmtime::component::ComponentExportIndex, + r6: wasmtime::component::ComponentExportIndex, + r7: wasmtime::component::ComponentExportIndex, + r8: wasmtime::component::ComponentExportIndex, + pair_ret: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/integers") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/integers`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/integers") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/integers`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/integers` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let a1 = lookup("a1")?; + let a2 = lookup("a2")?; + let a3 = lookup("a3")?; + let a4 = lookup("a4")?; + let a5 = lookup("a5")?; + let a6 = lookup("a6")?; + let a7 = lookup("a7")?; + let a8 = lookup("a8")?; + let a9 = lookup("a9")?; + let r1 = lookup("r1")?; + let r2 = lookup("r2")?; + let r3 = lookup("r3")?; + let r4 = lookup("r4")?; + let r5 = lookup("r5")?; + let r6 = lookup("r6")?; + let r7 = lookup("r7")?; + let r8 = lookup("r8")?; + let pair_ret = lookup("pair-ret")?; + Ok(GuestIndices { + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + pair_ret, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let a1 = *_instance + .get_typed_func::<(u8,), ()>(&mut store, &self.a1)? + .func(); + let a2 = *_instance + .get_typed_func::<(i8,), ()>(&mut store, &self.a2)? + .func(); + let a3 = *_instance + .get_typed_func::<(u16,), ()>(&mut store, &self.a3)? + .func(); + let a4 = *_instance + .get_typed_func::<(i16,), ()>(&mut store, &self.a4)? + .func(); + let a5 = *_instance + .get_typed_func::<(u32,), ()>(&mut store, &self.a5)? + .func(); + let a6 = *_instance + .get_typed_func::<(i32,), ()>(&mut store, &self.a6)? + .func(); + let a7 = *_instance + .get_typed_func::<(u64,), ()>(&mut store, &self.a7)? + .func(); + let a8 = *_instance + .get_typed_func::<(i64,), ()>(&mut store, &self.a8)? + .func(); + let a9 = *_instance + .get_typed_func::< + (u8, i8, u16, i16, u32, i32, u64, i64), + (), + >(&mut store, &self.a9)? + .func(); + let r1 = *_instance + .get_typed_func::<(), (u8,)>(&mut store, &self.r1)? + .func(); + let r2 = *_instance + .get_typed_func::<(), (i8,)>(&mut store, &self.r2)? + .func(); + let r3 = *_instance + .get_typed_func::<(), (u16,)>(&mut store, &self.r3)? + .func(); + let r4 = *_instance + .get_typed_func::<(), (i16,)>(&mut store, &self.r4)? + .func(); + let r5 = *_instance + .get_typed_func::<(), (u32,)>(&mut store, &self.r5)? + .func(); + let r6 = *_instance + .get_typed_func::<(), (i32,)>(&mut store, &self.r6)? + .func(); + let r7 = *_instance + .get_typed_func::<(), (u64,)>(&mut store, &self.r7)? + .func(); + let r8 = *_instance + .get_typed_func::<(), (i64,)>(&mut store, &self.r8)? + .func(); + let pair_ret = *_instance + .get_typed_func::< + (), + ((i64, u8),), + >(&mut store, &self.pair_ret)? + .func(); + Ok(Guest { + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + pair_ret, + }) + } + } + impl Guest { + pub async fn call_a1( + &self, + mut store: S, + arg0: u8, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u8,), + (), + >::new_unchecked(self.a1) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a2( + &self, + mut store: S, + arg0: i8, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (i8,), + (), + >::new_unchecked(self.a2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a3( + &self, + mut store: S, + arg0: u16, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u16,), + (), + >::new_unchecked(self.a3) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a4( + &self, + mut store: S, + arg0: i16, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (i16,), + (), + >::new_unchecked(self.a4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a5( + &self, + mut store: S, + arg0: u32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u32,), + (), + >::new_unchecked(self.a5) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a6( + &self, + mut store: S, + arg0: i32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (i32,), + (), + >::new_unchecked(self.a6) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a7( + &self, + mut store: S, + arg0: u64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u64,), + (), + >::new_unchecked(self.a7) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a8( + &self, + mut store: S, + arg0: i64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (i64,), + (), + >::new_unchecked(self.a8) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_a9( + &self, + mut store: S, + arg0: u8, + arg1: i8, + arg2: u16, + arg3: i16, + arg4: u32, + arg5: i32, + arg6: u64, + arg7: i64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u8, i8, u16, i16, u32, i32, u64, i64), + (), + >::new_unchecked(self.a9) + }; + let promise = callee + .call_concurrent( + store.as_context_mut(), + (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), + ) + .await?; + Ok(promise) + } + pub async fn call_r1( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u8,), + >::new_unchecked(self.r1) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r2( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (i8,), + >::new_unchecked(self.r2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r3( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u16,), + >::new_unchecked(self.r3) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r4( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (i16,), + >::new_unchecked(self.r4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r5( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32,), + >::new_unchecked(self.r5) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r6( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (i32,), + >::new_unchecked(self.r6) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r7( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u64,), + >::new_unchecked(self.r7) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_r8( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (i64,), + >::new_unchecked(self.r8) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_pair_ret( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ((i64, u8),), + >::new_unchecked(self.pair_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/integers_tracing_async.rs b/crates/component-macro/tests/expanded/integers_tracing_async.rs index a8778d525cb6..77a7bea0d9da 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 bf131e0b4b01..33d53d9e9edf 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 aa55a3f6503e..1fd28a487961 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_concurrent.rs b/crates/component-macro/tests/expanded/lists_concurrent.rs new file mode 100644 index 000000000000..8146f9bf6c0a --- /dev/null +++ b/crates/component-macro/tests/expanded/lists_concurrent.rs @@ -0,0 +1,3431 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-lists`. +/// +/// This structure is created through [`TheListsPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheLists`] as well. +pub struct TheListsPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheListsIndices, +} +impl Clone for TheListsPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheListsPre<_T> { + /// Creates a new copy of `TheListsPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheListsIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheLists`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-lists`. +/// +/// This is an implementation detail of [`TheListsPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheLists`] as well. +#[derive(Clone)] +pub struct TheListsIndices { + interface0: exports::foo::foo::lists::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-lists`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheLists::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheListsPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheListsPre::instantiate_async`] to +/// create a [`TheLists`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheLists::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheListsIndices::new_instance`] followed +/// by [`TheListsIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheLists { + interface0: exports::foo::foo::lists::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheListsIndices { + /// Creates a new copy of `TheListsIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::lists::GuestIndices::new(_component)?; + Ok(TheListsIndices { interface0 }) + } + /// Creates a new instance of [`TheListsIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheLists`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheLists`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::lists::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheListsIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheLists`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheLists { interface0 }) + } + } + impl TheLists { + /// Convenience wrapper around [`TheListsPre::new`] and + /// [`TheListsPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheListsPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheListsIndices::new_instance`] and + /// [`TheListsIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheListsIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::lists::Host + 'static, + U: Send + foo::foo::lists::Host, + { + foo::foo::lists::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_lists(&self) -> &exports::foo::foo::lists::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod lists { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct OtherRecord { + #[component(name = "a1")] + pub a1: u32, + #[component(name = "a2")] + pub a2: u64, + #[component(name = "a3")] + pub a3: i32, + #[component(name = "a4")] + pub a4: i64, + #[component(name = "b")] + pub b: wasmtime::component::__internal::String, + #[component(name = "c")] + pub c: wasmtime::component::__internal::Vec, + } + impl core::fmt::Debug for OtherRecord { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("OtherRecord") + .field("a1", &self.a1) + .field("a2", &self.a2) + .field("a3", &self.a3) + .field("a4", &self.a4) + .field("b", &self.b) + .field("c", &self.c) + .finish() + } + } + const _: () = { + assert!( + 48 == < OtherRecord as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < OtherRecord as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct SomeRecord { + #[component(name = "x")] + pub x: wasmtime::component::__internal::String, + #[component(name = "y")] + pub y: OtherRecord, + #[component(name = "z")] + pub z: wasmtime::component::__internal::Vec, + #[component(name = "c1")] + pub c1: u32, + #[component(name = "c2")] + pub c2: u64, + #[component(name = "c3")] + pub c3: i32, + #[component(name = "c4")] + pub c4: i64, + } + impl core::fmt::Debug for SomeRecord { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("SomeRecord") + .field("x", &self.x) + .field("y", &self.y) + .field("z", &self.z) + .field("c1", &self.c1) + .field("c2", &self.c2) + .field("c3", &self.c3) + .field("c4", &self.c4) + .finish() + } + } + const _: () = { + assert!( + 96 == < SomeRecord as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < SomeRecord as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum OtherVariant { + #[component(name = "a")] + A, + #[component(name = "b")] + B(u32), + #[component(name = "c")] + C(wasmtime::component::__internal::String), + } + impl core::fmt::Debug for OtherVariant { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + OtherVariant::A => f.debug_tuple("OtherVariant::A").finish(), + OtherVariant::B(e) => { + f.debug_tuple("OtherVariant::B").field(e).finish() + } + OtherVariant::C(e) => { + f.debug_tuple("OtherVariant::C").field(e).finish() + } + } + } + } + const _: () = { + assert!( + 12 == < OtherVariant as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < OtherVariant as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum SomeVariant { + #[component(name = "a")] + A(wasmtime::component::__internal::String), + #[component(name = "b")] + B, + #[component(name = "c")] + C(u32), + #[component(name = "d")] + D(wasmtime::component::__internal::Vec), + } + impl core::fmt::Debug for SomeVariant { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + SomeVariant::A(e) => { + f.debug_tuple("SomeVariant::A").field(e).finish() + } + SomeVariant::B => f.debug_tuple("SomeVariant::B").finish(), + SomeVariant::C(e) => { + f.debug_tuple("SomeVariant::C").field(e).finish() + } + SomeVariant::D(e) => { + f.debug_tuple("SomeVariant::D").field(e).finish() + } + } + } + } + const _: () = { + assert!( + 12 == < SomeVariant as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < SomeVariant as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub type LoadStoreAllSizes = wasmtime::component::__internal::Vec< + ( + wasmtime::component::__internal::String, + u8, + i8, + u16, + i16, + u32, + i32, + u64, + i64, + f32, + f64, + char, + ), + >; + const _: () = { + assert!( + 8 == < LoadStoreAllSizes as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < LoadStoreAllSizes as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub trait Host { + type Data; + fn list_u8_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u16_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s8_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s16_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_f32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_f64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u8_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u16_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u16, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_u64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s8_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s16_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i16, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_s64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_f32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + f32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_f64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + f64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec<(u8, i8)>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + (i64, u32), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn string_list_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn string_list_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_string_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + (u8, wasmtime::component::__internal::String), + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn string_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn record_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + OtherRecord, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn record_list_reverse( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + SomeRecord, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn variant_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + OtherVariant, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn load_store_everything( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: LoadStoreAllSizes, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> LoadStoreAllSizes + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/lists")?; + inst.func_wrap_concurrent( + "list-u8-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_u8_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u16-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_u16_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u32-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_u32_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u64-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_u64_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s8-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_s8_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s16-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_s16_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s32-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_s32_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s64-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_s64_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-f32-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_f32_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-f64-param", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::list_f64_param(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u8-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_u8_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u16-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_u16_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u32-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_u32_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-u64-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_u64_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s8-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_s8_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s16-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_s16_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s32-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_s32_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-s64-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_s64_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-f32-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_f32_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-f64-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_f64_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-list", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec<(u8, i8)>,)| + { + let host = caller; + let r = ::tuple_list(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec<(i64, u32)>,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec<(i64, u32)>,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "string-list-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + )| + { + let host = caller; + let r = ::string_list_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "string-list-ret", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::string_list_ret(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-string-list", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + (u8, wasmtime::component::__internal::String), + >, + )| + { + let host = caller; + let r = ::tuple_string_list(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "string-list", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + )| + { + let host = caller; + let r = ::string_list(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "record-list", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::record_list(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "record-list-reverse", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::record_list_reverse(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "variant-list", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::variant_list(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "load-store-everything", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (LoadStoreAllSizes,)| + { + let host = caller; + let r = ::load_store_everything(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(LoadStoreAllSizes,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(LoadStoreAllSizes,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn list_u8_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u8_param(store, x) + } + fn list_u16_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u16_param(store, x) + } + fn list_u32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u32_param(store, x) + } + fn list_u64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u64_param(store, x) + } + fn list_s8_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s8_param(store, x) + } + fn list_s16_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s16_param(store, x) + } + fn list_s32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s32_param(store, x) + } + fn list_s64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s64_param(store, x) + } + fn list_f32_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_f32_param(store, x) + } + fn list_f64_param( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_f64_param(store, x) + } + fn list_u8_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u8_ret(store) + } + fn list_u16_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u16, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u16_ret(store) + } + fn list_u32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u32_ret(store) + } + fn list_u64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_u64_ret(store) + } + fn list_s8_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s8_ret(store) + } + fn list_s16_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i16, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s16_ret(store) + } + fn list_s32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s32_ret(store) + } + fn list_s64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + i64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_s64_ret(store) + } + fn list_f32_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + f32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_f32_ret(store) + } + fn list_f64_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + f64, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_f64_ret(store) + } + fn tuple_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec<(u8, i8)>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + (i64, u32), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_list(store, x) + } + fn string_list_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::string_list_arg(store, a) + } + fn string_list_ret( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::string_list_ret(store) + } + fn tuple_string_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + (u8, wasmtime::component::__internal::String), + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_string_list(store, x) + } + fn string_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::string_list(store, x) + } + fn record_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + OtherRecord, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::record_list(store, x) + } + fn record_list_reverse( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + SomeRecord, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::record_list_reverse(store, x) + } + fn variant_list( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + OtherVariant, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::variant_list(store, x) + } + fn load_store_everything( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: LoadStoreAllSizes, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> LoadStoreAllSizes + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::load_store_everything(store, a) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod lists { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct OtherRecord { + #[component(name = "a1")] + pub a1: u32, + #[component(name = "a2")] + pub a2: u64, + #[component(name = "a3")] + pub a3: i32, + #[component(name = "a4")] + pub a4: i64, + #[component(name = "b")] + pub b: wasmtime::component::__internal::String, + #[component(name = "c")] + pub c: wasmtime::component::__internal::Vec, + } + impl core::fmt::Debug for OtherRecord { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("OtherRecord") + .field("a1", &self.a1) + .field("a2", &self.a2) + .field("a3", &self.a3) + .field("a4", &self.a4) + .field("b", &self.b) + .field("c", &self.c) + .finish() + } + } + const _: () = { + assert!( + 48 == < OtherRecord as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 8 == < OtherRecord as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct SomeRecord { + #[component(name = "x")] + pub x: wasmtime::component::__internal::String, + #[component(name = "y")] + pub y: OtherRecord, + #[component(name = "z")] + pub z: wasmtime::component::__internal::Vec, + #[component(name = "c1")] + pub c1: u32, + #[component(name = "c2")] + pub c2: u64, + #[component(name = "c3")] + pub c3: i32, + #[component(name = "c4")] + pub c4: i64, + } + impl core::fmt::Debug for SomeRecord { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("SomeRecord") + .field("x", &self.x) + .field("y", &self.y) + .field("z", &self.z) + .field("c1", &self.c1) + .field("c2", &self.c2) + .field("c3", &self.c3) + .field("c4", &self.c4) + .finish() + } + } + const _: () = { + assert!( + 96 == < SomeRecord as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 8 == < SomeRecord as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum OtherVariant { + #[component(name = "a")] + A, + #[component(name = "b")] + B(u32), + #[component(name = "c")] + C(wasmtime::component::__internal::String), + } + impl core::fmt::Debug for OtherVariant { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + OtherVariant::A => f.debug_tuple("OtherVariant::A").finish(), + OtherVariant::B(e) => { + f.debug_tuple("OtherVariant::B").field(e).finish() + } + OtherVariant::C(e) => { + f.debug_tuple("OtherVariant::C").field(e).finish() + } + } + } + } + const _: () = { + assert!( + 12 == < OtherVariant as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < OtherVariant as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum SomeVariant { + #[component(name = "a")] + A(wasmtime::component::__internal::String), + #[component(name = "b")] + B, + #[component(name = "c")] + C(u32), + #[component(name = "d")] + D(wasmtime::component::__internal::Vec), + } + impl core::fmt::Debug for SomeVariant { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + SomeVariant::A(e) => { + f.debug_tuple("SomeVariant::A").field(e).finish() + } + SomeVariant::B => f.debug_tuple("SomeVariant::B").finish(), + SomeVariant::C(e) => { + f.debug_tuple("SomeVariant::C").field(e).finish() + } + SomeVariant::D(e) => { + f.debug_tuple("SomeVariant::D").field(e).finish() + } + } + } + } + const _: () = { + assert!( + 12 == < SomeVariant as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < SomeVariant as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub type LoadStoreAllSizes = wasmtime::component::__internal::Vec< + ( + wasmtime::component::__internal::String, + u8, + i8, + u16, + i16, + u32, + i32, + u64, + i64, + f32, + f64, + char, + ), + >; + const _: () = { + assert!( + 8 == < LoadStoreAllSizes as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < LoadStoreAllSizes as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub struct Guest { + list_u8_param: wasmtime::component::Func, + list_u16_param: wasmtime::component::Func, + list_u32_param: wasmtime::component::Func, + list_u64_param: wasmtime::component::Func, + list_s8_param: wasmtime::component::Func, + list_s16_param: wasmtime::component::Func, + list_s32_param: wasmtime::component::Func, + list_s64_param: wasmtime::component::Func, + list_f32_param: wasmtime::component::Func, + list_f64_param: wasmtime::component::Func, + list_u8_ret: wasmtime::component::Func, + list_u16_ret: wasmtime::component::Func, + list_u32_ret: wasmtime::component::Func, + list_u64_ret: wasmtime::component::Func, + list_s8_ret: wasmtime::component::Func, + list_s16_ret: wasmtime::component::Func, + list_s32_ret: wasmtime::component::Func, + list_s64_ret: wasmtime::component::Func, + list_f32_ret: wasmtime::component::Func, + list_f64_ret: wasmtime::component::Func, + tuple_list: wasmtime::component::Func, + string_list_arg: wasmtime::component::Func, + string_list_ret: wasmtime::component::Func, + tuple_string_list: wasmtime::component::Func, + string_list: wasmtime::component::Func, + record_list: wasmtime::component::Func, + record_list_reverse: wasmtime::component::Func, + variant_list: wasmtime::component::Func, + load_store_everything: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + list_u8_param: wasmtime::component::ComponentExportIndex, + list_u16_param: wasmtime::component::ComponentExportIndex, + list_u32_param: wasmtime::component::ComponentExportIndex, + list_u64_param: wasmtime::component::ComponentExportIndex, + list_s8_param: wasmtime::component::ComponentExportIndex, + list_s16_param: wasmtime::component::ComponentExportIndex, + list_s32_param: wasmtime::component::ComponentExportIndex, + list_s64_param: wasmtime::component::ComponentExportIndex, + list_f32_param: wasmtime::component::ComponentExportIndex, + list_f64_param: wasmtime::component::ComponentExportIndex, + list_u8_ret: wasmtime::component::ComponentExportIndex, + list_u16_ret: wasmtime::component::ComponentExportIndex, + list_u32_ret: wasmtime::component::ComponentExportIndex, + list_u64_ret: wasmtime::component::ComponentExportIndex, + list_s8_ret: wasmtime::component::ComponentExportIndex, + list_s16_ret: wasmtime::component::ComponentExportIndex, + list_s32_ret: wasmtime::component::ComponentExportIndex, + list_s64_ret: wasmtime::component::ComponentExportIndex, + list_f32_ret: wasmtime::component::ComponentExportIndex, + list_f64_ret: wasmtime::component::ComponentExportIndex, + tuple_list: wasmtime::component::ComponentExportIndex, + string_list_arg: wasmtime::component::ComponentExportIndex, + string_list_ret: wasmtime::component::ComponentExportIndex, + tuple_string_list: wasmtime::component::ComponentExportIndex, + string_list: wasmtime::component::ComponentExportIndex, + record_list: wasmtime::component::ComponentExportIndex, + record_list_reverse: wasmtime::component::ComponentExportIndex, + variant_list: wasmtime::component::ComponentExportIndex, + load_store_everything: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/lists") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/lists`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/lists") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/lists`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/lists` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let list_u8_param = lookup("list-u8-param")?; + let list_u16_param = lookup("list-u16-param")?; + let list_u32_param = lookup("list-u32-param")?; + let list_u64_param = lookup("list-u64-param")?; + let list_s8_param = lookup("list-s8-param")?; + let list_s16_param = lookup("list-s16-param")?; + let list_s32_param = lookup("list-s32-param")?; + let list_s64_param = lookup("list-s64-param")?; + let list_f32_param = lookup("list-f32-param")?; + let list_f64_param = lookup("list-f64-param")?; + let list_u8_ret = lookup("list-u8-ret")?; + let list_u16_ret = lookup("list-u16-ret")?; + let list_u32_ret = lookup("list-u32-ret")?; + let list_u64_ret = lookup("list-u64-ret")?; + let list_s8_ret = lookup("list-s8-ret")?; + let list_s16_ret = lookup("list-s16-ret")?; + let list_s32_ret = lookup("list-s32-ret")?; + let list_s64_ret = lookup("list-s64-ret")?; + let list_f32_ret = lookup("list-f32-ret")?; + let list_f64_ret = lookup("list-f64-ret")?; + let tuple_list = lookup("tuple-list")?; + let string_list_arg = lookup("string-list-arg")?; + let string_list_ret = lookup("string-list-ret")?; + let tuple_string_list = lookup("tuple-string-list")?; + let string_list = lookup("string-list")?; + let record_list = lookup("record-list")?; + let record_list_reverse = lookup("record-list-reverse")?; + let variant_list = lookup("variant-list")?; + let load_store_everything = lookup("load-store-everything")?; + Ok(GuestIndices { + list_u8_param, + list_u16_param, + list_u32_param, + list_u64_param, + list_s8_param, + list_s16_param, + list_s32_param, + list_s64_param, + list_f32_param, + list_f64_param, + list_u8_ret, + list_u16_ret, + list_u32_ret, + list_u64_ret, + list_s8_ret, + list_s16_ret, + list_s32_ret, + list_s64_ret, + list_f32_ret, + list_f64_ret, + tuple_list, + string_list_arg, + string_list_ret, + tuple_string_list, + string_list, + record_list, + record_list_reverse, + variant_list, + load_store_everything, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let list_u8_param = *_instance + .get_typed_func::< + (&[u8],), + (), + >(&mut store, &self.list_u8_param)? + .func(); + let list_u16_param = *_instance + .get_typed_func::< + (&[u16],), + (), + >(&mut store, &self.list_u16_param)? + .func(); + let list_u32_param = *_instance + .get_typed_func::< + (&[u32],), + (), + >(&mut store, &self.list_u32_param)? + .func(); + let list_u64_param = *_instance + .get_typed_func::< + (&[u64],), + (), + >(&mut store, &self.list_u64_param)? + .func(); + let list_s8_param = *_instance + .get_typed_func::< + (&[i8],), + (), + >(&mut store, &self.list_s8_param)? + .func(); + let list_s16_param = *_instance + .get_typed_func::< + (&[i16],), + (), + >(&mut store, &self.list_s16_param)? + .func(); + let list_s32_param = *_instance + .get_typed_func::< + (&[i32],), + (), + >(&mut store, &self.list_s32_param)? + .func(); + let list_s64_param = *_instance + .get_typed_func::< + (&[i64],), + (), + >(&mut store, &self.list_s64_param)? + .func(); + let list_f32_param = *_instance + .get_typed_func::< + (&[f32],), + (), + >(&mut store, &self.list_f32_param)? + .func(); + let list_f64_param = *_instance + .get_typed_func::< + (&[f64],), + (), + >(&mut store, &self.list_f64_param)? + .func(); + let list_u8_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_u8_ret)? + .func(); + let list_u16_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_u16_ret)? + .func(); + let list_u32_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_u32_ret)? + .func(); + let list_u64_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_u64_ret)? + .func(); + let list_s8_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_s8_ret)? + .func(); + let list_s16_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_s16_ret)? + .func(); + let list_s32_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_s32_ret)? + .func(); + let list_s64_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_s64_ret)? + .func(); + let list_f32_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_f32_ret)? + .func(); + let list_f64_ret = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.list_f64_ret)? + .func(); + let tuple_list = *_instance + .get_typed_func::< + (&[(u8, i8)],), + (wasmtime::component::__internal::Vec<(i64, u32)>,), + >(&mut store, &self.tuple_list)? + .func(); + let string_list_arg = *_instance + .get_typed_func::< + (&[wasmtime::component::__internal::String],), + (), + >(&mut store, &self.string_list_arg)? + .func(); + let string_list_ret = *_instance + .get_typed_func::< + (), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + >(&mut store, &self.string_list_ret)? + .func(); + let tuple_string_list = *_instance + .get_typed_func::< + (&[(u8, wasmtime::component::__internal::String)],), + ( + wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + ), + >(&mut store, &self.tuple_string_list)? + .func(); + let string_list = *_instance + .get_typed_func::< + (&[wasmtime::component::__internal::String],), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + >(&mut store, &self.string_list)? + .func(); + let record_list = *_instance + .get_typed_func::< + (&[SomeRecord],), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.record_list)? + .func(); + let record_list_reverse = *_instance + .get_typed_func::< + (&[OtherRecord],), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.record_list_reverse)? + .func(); + let variant_list = *_instance + .get_typed_func::< + (&[SomeVariant],), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.variant_list)? + .func(); + let load_store_everything = *_instance + .get_typed_func::< + (&LoadStoreAllSizes,), + (LoadStoreAllSizes,), + >(&mut store, &self.load_store_everything)? + .func(); + Ok(Guest { + list_u8_param, + list_u16_param, + list_u32_param, + list_u64_param, + list_s8_param, + list_s16_param, + list_s32_param, + list_s64_param, + list_f32_param, + list_f64_param, + list_u8_ret, + list_u16_ret, + list_u32_ret, + list_u64_ret, + list_s8_ret, + list_s16_ret, + list_s32_ret, + list_s64_ret, + list_f32_ret, + list_f64_ret, + tuple_list, + string_list_arg, + string_list_ret, + tuple_string_list, + string_list, + record_list, + record_list_reverse, + variant_list, + load_store_everything, + }) + } + } + impl Guest { + pub async fn call_list_u8_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_u8_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_u16_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_u16_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_u32_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_u32_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_u64_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_u64_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_s8_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_s8_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_s16_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_s16_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_s32_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_s32_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_s64_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_s64_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_f32_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_f32_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_f64_param( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.list_f64_param) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_list_u8_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_u8_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_u16_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_u16_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_u32_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_u32_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_u64_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_u64_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_s8_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_s8_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_s16_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_s16_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_s32_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_s32_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_s64_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_s64_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_f32_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_f32_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_list_f64_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.list_f64_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_tuple_list( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec<(u8, i8)>, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec<(i64, u32)>, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec<(u8, i8)>,), + (wasmtime::component::__internal::Vec<(i64, u32)>,), + >::new_unchecked(self.tuple_list) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_string_list_arg( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + (), + >::new_unchecked(self.string_list_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_string_list_ret( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + >::new_unchecked(self.string_list_ret) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_tuple_string_list( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec< + (u8, wasmtime::component::__internal::String), + >, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::Vec< + (u8, wasmtime::component::__internal::String), + >, + ), + ( + wasmtime::component::__internal::Vec< + (wasmtime::component::__internal::String, u8), + >, + ), + >::new_unchecked(self.tuple_string_list) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_string_list( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::String, + >, + ), + >::new_unchecked(self.string_list) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_record_list( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.record_list) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_record_list_reverse( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.record_list_reverse) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_variant_list( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.variant_list) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_load_store_everything( + &self, + mut store: S, + arg0: LoadStoreAllSizes, + ) -> wasmtime::Result< + wasmtime::component::Promise, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (LoadStoreAllSizes,), + (LoadStoreAllSizes,), + >::new_unchecked(self.load_store_everything) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/lists_tracing_async.rs b/crates/component-macro/tests/expanded/lists_tracing_async.rs index a1e470bea83f..8f12eaae92ae 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 615876268b88..678dea3c94b3 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 1f454d623998..c2ec821b48c2 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_concurrent.rs b/crates/component-macro/tests/expanded/many-arguments_concurrent.rs new file mode 100644 index 000000000000..81faf00b9857 --- /dev/null +++ b/crates/component-macro/tests/expanded/many-arguments_concurrent.rs @@ -0,0 +1,827 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::manyarg::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::manyarg::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::manyarg::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::manyarg::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::manyarg::Host + 'static, + U: Send + foo::foo::manyarg::Host, + { + foo::foo::manyarg::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_manyarg(&self) -> &exports::foo::foo::manyarg::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod manyarg { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct BigStruct { + #[component(name = "a1")] + pub a1: wasmtime::component::__internal::String, + #[component(name = "a2")] + pub a2: wasmtime::component::__internal::String, + #[component(name = "a3")] + pub a3: wasmtime::component::__internal::String, + #[component(name = "a4")] + pub a4: wasmtime::component::__internal::String, + #[component(name = "a5")] + pub a5: wasmtime::component::__internal::String, + #[component(name = "a6")] + pub a6: wasmtime::component::__internal::String, + #[component(name = "a7")] + pub a7: wasmtime::component::__internal::String, + #[component(name = "a8")] + pub a8: wasmtime::component::__internal::String, + #[component(name = "a9")] + pub a9: wasmtime::component::__internal::String, + #[component(name = "a10")] + pub a10: wasmtime::component::__internal::String, + #[component(name = "a11")] + pub a11: wasmtime::component::__internal::String, + #[component(name = "a12")] + pub a12: wasmtime::component::__internal::String, + #[component(name = "a13")] + pub a13: wasmtime::component::__internal::String, + #[component(name = "a14")] + pub a14: wasmtime::component::__internal::String, + #[component(name = "a15")] + pub a15: wasmtime::component::__internal::String, + #[component(name = "a16")] + pub a16: wasmtime::component::__internal::String, + #[component(name = "a17")] + pub a17: wasmtime::component::__internal::String, + #[component(name = "a18")] + pub a18: wasmtime::component::__internal::String, + #[component(name = "a19")] + pub a19: wasmtime::component::__internal::String, + #[component(name = "a20")] + pub a20: wasmtime::component::__internal::String, + } + impl core::fmt::Debug for BigStruct { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("BigStruct") + .field("a1", &self.a1) + .field("a2", &self.a2) + .field("a3", &self.a3) + .field("a4", &self.a4) + .field("a5", &self.a5) + .field("a6", &self.a6) + .field("a7", &self.a7) + .field("a8", &self.a8) + .field("a9", &self.a9) + .field("a10", &self.a10) + .field("a11", &self.a11) + .field("a12", &self.a12) + .field("a13", &self.a13) + .field("a14", &self.a14) + .field("a15", &self.a15) + .field("a16", &self.a16) + .field("a17", &self.a17) + .field("a18", &self.a18) + .field("a19", &self.a19) + .field("a20", &self.a20) + .finish() + } + } + const _: () = { + assert!( + 160 == < BigStruct as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub trait Host { + type Data; + fn many_args( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn big_argument( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: BigStruct, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/manyarg")?; + inst.func_wrap_concurrent( + "many-args", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + arg15, + ): ( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + )| + { + let host = caller; + let r = ::many_args( + host, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + arg15, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "big-argument", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (BigStruct,)| + { + let host = caller; + let r = ::big_argument(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn many_args( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::many_args( + store, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + ) + } + fn big_argument( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: BigStruct, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::big_argument(store, x) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod manyarg { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct BigStruct { + #[component(name = "a1")] + pub a1: wasmtime::component::__internal::String, + #[component(name = "a2")] + pub a2: wasmtime::component::__internal::String, + #[component(name = "a3")] + pub a3: wasmtime::component::__internal::String, + #[component(name = "a4")] + pub a4: wasmtime::component::__internal::String, + #[component(name = "a5")] + pub a5: wasmtime::component::__internal::String, + #[component(name = "a6")] + pub a6: wasmtime::component::__internal::String, + #[component(name = "a7")] + pub a7: wasmtime::component::__internal::String, + #[component(name = "a8")] + pub a8: wasmtime::component::__internal::String, + #[component(name = "a9")] + pub a9: wasmtime::component::__internal::String, + #[component(name = "a10")] + pub a10: wasmtime::component::__internal::String, + #[component(name = "a11")] + pub a11: wasmtime::component::__internal::String, + #[component(name = "a12")] + pub a12: wasmtime::component::__internal::String, + #[component(name = "a13")] + pub a13: wasmtime::component::__internal::String, + #[component(name = "a14")] + pub a14: wasmtime::component::__internal::String, + #[component(name = "a15")] + pub a15: wasmtime::component::__internal::String, + #[component(name = "a16")] + pub a16: wasmtime::component::__internal::String, + #[component(name = "a17")] + pub a17: wasmtime::component::__internal::String, + #[component(name = "a18")] + pub a18: wasmtime::component::__internal::String, + #[component(name = "a19")] + pub a19: wasmtime::component::__internal::String, + #[component(name = "a20")] + pub a20: wasmtime::component::__internal::String, + } + impl core::fmt::Debug for BigStruct { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("BigStruct") + .field("a1", &self.a1) + .field("a2", &self.a2) + .field("a3", &self.a3) + .field("a4", &self.a4) + .field("a5", &self.a5) + .field("a6", &self.a6) + .field("a7", &self.a7) + .field("a8", &self.a8) + .field("a9", &self.a9) + .field("a10", &self.a10) + .field("a11", &self.a11) + .field("a12", &self.a12) + .field("a13", &self.a13) + .field("a14", &self.a14) + .field("a15", &self.a15) + .field("a16", &self.a16) + .field("a17", &self.a17) + .field("a18", &self.a18) + .field("a19", &self.a19) + .field("a20", &self.a20) + .finish() + } + } + const _: () = { + assert!( + 160 == < BigStruct as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < BigStruct as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub struct Guest { + many_args: wasmtime::component::Func, + big_argument: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + many_args: wasmtime::component::ComponentExportIndex, + big_argument: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/manyarg") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/manyarg`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/manyarg") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/manyarg`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/manyarg` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let many_args = lookup("many-args")?; + let big_argument = lookup("big-argument")?; + Ok(GuestIndices { + many_args, + big_argument, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let many_args = *_instance + .get_typed_func::< + ( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ), + (), + >(&mut store, &self.many_args)? + .func(); + let big_argument = *_instance + .get_typed_func::< + (&BigStruct,), + (), + >(&mut store, &self.big_argument)? + .func(); + Ok(Guest { many_args, big_argument }) + } + } + impl Guest { + pub async fn call_many_args( + &self, + mut store: S, + arg0: u64, + arg1: u64, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, + arg6: u64, + arg7: u64, + arg8: u64, + arg9: u64, + arg10: u64, + arg11: u64, + arg12: u64, + arg13: u64, + arg14: u64, + arg15: u64, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ), + (), + >::new_unchecked(self.many_args) + }; + let promise = callee + .call_concurrent( + store.as_context_mut(), + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + arg15, + ), + ) + .await?; + Ok(promise) + } + pub async fn call_big_argument( + &self, + mut store: S, + arg0: BigStruct, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (BigStruct,), + (), + >::new_unchecked(self.big_argument) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + } + } + } + } +} 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 334616870f8a..7328079a2ba3 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 dd0637fbeb4b..6b49a8133aad 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 2b434520c89c..29d254868d8a 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_concurrent.rs b/crates/component-macro/tests/expanded/multi-return_concurrent.rs new file mode 100644 index 000000000000..11172f167cc7 --- /dev/null +++ b/crates/component-macro/tests/expanded/multi-return_concurrent.rs @@ -0,0 +1,704 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::multi_return::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::multi_return::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::multi_return::GuestIndices::new( + _component, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::multi_return::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::multi_return::Host + 'static, + U: Send + foo::foo::multi_return::Host, + { + foo::foo::multi_return::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_multi_return(&self) -> &exports::foo::foo::multi_return::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod multi_return { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn mra( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn mrb( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn mrc( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn mrd( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn mre( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, f32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/multi-return")?; + inst.func_wrap_concurrent( + "mra", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::mra(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "mrb", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::mrb(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "mrc", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::mrc(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "mrd", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::mrd(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "mre", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::mre(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32, f32)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32, f32)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn mra( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::mra(store) + } + fn mrb( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::mrb(store) + } + fn mrc( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::mrc(store) + } + fn mrd( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::mrd(store) + } + fn mre( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, f32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::mre(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod multi_return { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + mra: wasmtime::component::Func, + mrb: wasmtime::component::Func, + mrc: wasmtime::component::Func, + mrd: wasmtime::component::Func, + mre: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + mra: wasmtime::component::ComponentExportIndex, + mrb: wasmtime::component::ComponentExportIndex, + mrc: wasmtime::component::ComponentExportIndex, + mrd: wasmtime::component::ComponentExportIndex, + mre: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/multi-return") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/multi-return`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/multi-return") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/multi-return`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/multi-return` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let mra = lookup("mra")?; + let mrb = lookup("mrb")?; + let mrc = lookup("mrc")?; + let mrd = lookup("mrd")?; + let mre = lookup("mre")?; + Ok(GuestIndices { + mra, + mrb, + mrc, + mrd, + mre, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let mra = *_instance + .get_typed_func::<(), ()>(&mut store, &self.mra)? + .func(); + let mrb = *_instance + .get_typed_func::<(), ()>(&mut store, &self.mrb)? + .func(); + let mrc = *_instance + .get_typed_func::<(), (u32,)>(&mut store, &self.mrc)? + .func(); + let mrd = *_instance + .get_typed_func::<(), (u32,)>(&mut store, &self.mrd)? + .func(); + let mre = *_instance + .get_typed_func::<(), (u32, f32)>(&mut store, &self.mre)? + .func(); + Ok(Guest { mra, mrb, mrc, mrd, mre }) + } + } + impl Guest { + pub async fn call_mra( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.mra) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_mrb( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.mrb) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_mrc( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32,), + >::new_unchecked(self.mrc) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_mrd( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32,), + >::new_unchecked(self.mrd) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_mre( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32, f32), + >::new_unchecked(self.mre) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/multi-return_tracing_async.rs b/crates/component-macro/tests/expanded/multi-return_tracing_async.rs index ca9c503e58b7..da628f4e7b28 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 da50b12799ea..d093cb5fa1c7 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 885c0ce1b69b..7b4119382ec9 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_concurrent.rs b/crates/component-macro/tests/expanded/multiversion_concurrent.rs new file mode 100644 index 000000000000..d3839a27eb28 --- /dev/null +++ b/crates/component-macro/tests/expanded/multiversion_concurrent.rs @@ -0,0 +1,619 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `foo`. +/// +/// This structure is created through [`FooPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Foo`] as well. +pub struct FooPre { + instance_pre: wasmtime::component::InstancePre, + indices: FooIndices, +} +impl Clone for FooPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Creates a new copy of `FooPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = FooIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Foo`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `foo`. +/// +/// This is an implementation detail of [`FooPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Foo`] as well. +#[derive(Clone)] +pub struct FooIndices { + interface0: exports::my::dep0_1_0::a::GuestIndices, + interface1: exports::my::dep0_2_0::a::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `foo`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Foo::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`FooPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`FooPre::instantiate_async`] to +/// create a [`Foo`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Foo::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`FooIndices::new_instance`] followed +/// by [`FooIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Foo { + interface0: exports::my::dep0_1_0::a::Guest, + interface1: exports::my::dep0_2_0::a::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl FooIndices { + /// Creates a new copy of `FooIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::my::dep0_1_0::a::GuestIndices::new(_component)?; + let interface1 = exports::my::dep0_2_0::a::GuestIndices::new(_component)?; + Ok(FooIndices { + interface0, + interface1, + }) + } + /// Creates a new instance of [`FooIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Foo`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Foo`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::my::dep0_1_0::a::GuestIndices::new_instance( + &mut store, + _instance, + )?; + let interface1 = exports::my::dep0_2_0::a::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(FooIndices { + interface0, + interface1, + }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Foo`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + let interface1 = self.interface1.load(&mut store, &_instance)?; + Ok(Foo { interface0, interface1 }) + } + } + impl Foo { + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`FooIndices::new_instance`] and + /// [`FooIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = FooIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + my::dep0_1_0::a::Host + my::dep0_2_0::a::Host + + 'static, + U: Send + my::dep0_1_0::a::Host + my::dep0_2_0::a::Host, + { + my::dep0_1_0::a::add_to_linker(linker, get)?; + my::dep0_2_0::a::add_to_linker(linker, get)?; + Ok(()) + } + pub fn my_dep0_1_0_a(&self) -> &exports::my::dep0_1_0::a::Guest { + &self.interface0 + } + pub fn my_dep0_2_0_a(&self) -> &exports::my::dep0_2_0::a::Guest { + &self.interface1 + } + } +}; +pub mod my { + pub mod dep0_1_0 { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn x( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("my:dep/a@0.1.0")?; + inst.func_wrap_concurrent( + "x", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::x(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn x( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::x(store) + } + } + } + } + pub mod dep0_2_0 { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn x( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("my:dep/a@0.2.0")?; + inst.func_wrap_concurrent( + "x", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::x(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn x( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::x(store) + } + } + } + } +} +pub mod exports { + pub mod my { + pub mod dep0_1_0 { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + x: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + x: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "my:dep/a@0.1.0") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `my:dep/a@0.1.0`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "my:dep/a@0.1.0") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `my:dep/a@0.1.0`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `my:dep/a@0.1.0` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let x = lookup("x")?; + Ok(GuestIndices { x }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let x = *_instance + .get_typed_func::<(), ()>(&mut store, &self.x)? + .func(); + Ok(Guest { x }) + } + } + impl Guest { + pub async fn call_x( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.x) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + } + } + } + pub mod dep0_2_0 { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + x: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + x: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "my:dep/a@0.2.0") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `my:dep/a@0.2.0`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "my:dep/a@0.2.0") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `my:dep/a@0.2.0`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `my:dep/a@0.2.0` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let x = lookup("x")?; + Ok(GuestIndices { x }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let x = *_instance + .get_typed_func::<(), ()>(&mut store, &self.x)? + .func(); + Ok(Guest { x }) + } + } + impl Guest { + pub async fn call_x( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.x) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/multiversion_tracing_async.rs b/crates/component-macro/tests/expanded/multiversion_tracing_async.rs index 76256e2fa92b..25cde1ded0e1 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 834a00afedb8..cdd90eeeba47 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 d4b1af2dee6d..ce8e77b8ebed 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_concurrent.rs b/crates/component-macro/tests/expanded/path1_concurrent.rs new file mode 100644 index 000000000000..ca4f7a3f6e3d --- /dev/null +++ b/crates/component-macro/tests/expanded/path1_concurrent.rs @@ -0,0 +1,220 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `path1`. +/// +/// This structure is created through [`Path1Pre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Path1`] as well. +pub struct Path1Pre { + instance_pre: wasmtime::component::InstancePre, + indices: Path1Indices, +} +impl Clone for Path1Pre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> Path1Pre<_T> { + /// Creates a new copy of `Path1Pre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = Path1Indices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Path1`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `path1`. +/// +/// This is an implementation detail of [`Path1Pre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Path1`] as well. +#[derive(Clone)] +pub struct Path1Indices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `path1`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Path1::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`Path1Pre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`Path1Pre::instantiate_async`] to +/// create a [`Path1`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Path1::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`Path1Indices::new_instance`] followed +/// by [`Path1Indices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Path1 {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl Path1Indices { + /// Creates a new copy of `Path1Indices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(Path1Indices {}) + } + /// Creates a new instance of [`Path1Indices`] from an + /// instantiated component. + /// + /// This method of creating a [`Path1`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Path1`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Path1Indices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Path1`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Path1 {}) + } + } + impl Path1 { + /// Convenience wrapper around [`Path1Pre::new`] and + /// [`Path1Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + Path1Pre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`Path1Indices::new_instance`] and + /// [`Path1Indices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = Path1Indices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + paths::path1::test::Host + 'static, + U: Send + paths::path1::test::Host, + { + paths::path1::test::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod paths { + pub mod path1 { + #[allow(clippy::all)] + pub mod test { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("paths:path1/test")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} diff --git a/crates/component-macro/tests/expanded/path1_tracing_async.rs b/crates/component-macro/tests/expanded/path1_tracing_async.rs index d4b1af2dee6d..ce8e77b8ebed 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 fd9b5460a9e8..4c676bd411ee 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 45fdb5d811b5..fc22e6b9df3f 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_concurrent.rs b/crates/component-macro/tests/expanded/path2_concurrent.rs new file mode 100644 index 000000000000..7aa6087f17dd --- /dev/null +++ b/crates/component-macro/tests/expanded/path2_concurrent.rs @@ -0,0 +1,220 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `path2`. +/// +/// This structure is created through [`Path2Pre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Path2`] as well. +pub struct Path2Pre { + instance_pre: wasmtime::component::InstancePre, + indices: Path2Indices, +} +impl Clone for Path2Pre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> Path2Pre<_T> { + /// Creates a new copy of `Path2Pre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = Path2Indices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Path2`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `path2`. +/// +/// This is an implementation detail of [`Path2Pre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Path2`] as well. +#[derive(Clone)] +pub struct Path2Indices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `path2`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Path2::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`Path2Pre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`Path2Pre::instantiate_async`] to +/// create a [`Path2`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Path2::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`Path2Indices::new_instance`] followed +/// by [`Path2Indices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Path2 {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl Path2Indices { + /// Creates a new copy of `Path2Indices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(Path2Indices {}) + } + /// Creates a new instance of [`Path2Indices`] from an + /// instantiated component. + /// + /// This method of creating a [`Path2`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Path2`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Path2Indices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Path2`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Path2 {}) + } + } + impl Path2 { + /// Convenience wrapper around [`Path2Pre::new`] and + /// [`Path2Pre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + Path2Pre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`Path2Indices::new_instance`] and + /// [`Path2Indices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = Path2Indices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + paths::path2::test::Host + 'static, + U: Send + paths::path2::test::Host, + { + paths::path2::test::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod paths { + pub mod path2 { + #[allow(clippy::all)] + pub mod test { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("paths:path2/test")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} diff --git a/crates/component-macro/tests/expanded/path2_tracing_async.rs b/crates/component-macro/tests/expanded/path2_tracing_async.rs index 45fdb5d811b5..fc22e6b9df3f 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 edca81d63efb..7c4c1463adf8 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 0c087de7ff66..c4f4ec274c71 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_concurrent.rs b/crates/component-macro/tests/expanded/records_concurrent.rs new file mode 100644 index 000000000000..bdf09ab04a1c --- /dev/null +++ b/crates/component-macro/tests/expanded/records_concurrent.rs @@ -0,0 +1,1555 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::records::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::records::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::records::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::records::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::records::Host + 'static, + U: Send + foo::foo::records::Host, + { + foo::foo::records::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_records(&self) -> &exports::foo::foo::records::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod records { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Empty {} + impl core::fmt::Debug for Empty { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Empty").finish() + } + } + const _: () = { + assert!(0 == < Empty as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Empty as wasmtime::component::ComponentType >::ALIGN32); + }; + /// A record containing two scalar fields + /// that both have the same type + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Scalars { + /// The first field, named a + #[component(name = "a")] + pub a: u32, + /// The second field, named b + #[component(name = "b")] + pub b: u32, + } + impl core::fmt::Debug for Scalars { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Scalars") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } + } + const _: () = { + assert!(8 == < Scalars as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Scalars as wasmtime::component::ComponentType >::ALIGN32); + }; + /// A record that is really just flags + /// All of the fields are bool + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct ReallyFlags { + #[component(name = "a")] + pub a: bool, + #[component(name = "b")] + pub b: bool, + #[component(name = "c")] + pub c: bool, + #[component(name = "d")] + pub d: bool, + #[component(name = "e")] + pub e: bool, + #[component(name = "f")] + pub f: bool, + #[component(name = "g")] + pub g: bool, + #[component(name = "h")] + pub h: bool, + #[component(name = "i")] + pub i: bool, + } + impl core::fmt::Debug for ReallyFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ReallyFlags") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .field("e", &self.e) + .field("f", &self.f) + .field("g", &self.g) + .field("h", &self.h) + .field("i", &self.i) + .finish() + } + } + const _: () = { + assert!( + 9 == < ReallyFlags as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < ReallyFlags as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct Aggregates { + #[component(name = "a")] + pub a: Scalars, + #[component(name = "b")] + pub b: u32, + #[component(name = "c")] + pub c: Empty, + #[component(name = "d")] + pub d: wasmtime::component::__internal::String, + #[component(name = "e")] + pub e: ReallyFlags, + } + impl core::fmt::Debug for Aggregates { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Aggregates") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .field("e", &self.e) + .finish() + } + } + const _: () = { + assert!( + 32 == < Aggregates as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Aggregates as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub type IntTypedef = i32; + const _: () = { + assert!( + 4 == < IntTypedef as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < IntTypedef as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub type TupleTypedef2 = (IntTypedef,); + const _: () = { + assert!( + 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub trait Host { + type Data; + fn tuple_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (char, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (char, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn empty_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Empty, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn empty_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Empty + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn scalar_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Scalars, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn scalar_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Scalars + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn flags_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: ReallyFlags, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn flags_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ReallyFlags + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn aggregate_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Aggregates, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn aggregate_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Aggregates + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn typedef_inout( + store: wasmtime::StoreContextMut<'_, Self::Data>, + e: TupleTypedef2, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/records")?; + inst.func_wrap_concurrent( + "tuple-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): ((char, u32),)| + { + let host = caller; + let r = ::tuple_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::tuple_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((char, u32),)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((char, u32),)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "empty-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Empty,)| + { + let host = caller; + let r = ::empty_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "empty-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::empty_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Empty,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Empty,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "scalar-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Scalars,)| + { + let host = caller; + let r = ::scalar_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "scalar-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::scalar_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Scalars,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Scalars,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "flags-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (ReallyFlags,)| + { + let host = caller; + let r = ::flags_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "flags-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::flags_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(ReallyFlags,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(ReallyFlags,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "aggregate-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Aggregates,)| + { + let host = caller; + let r = ::aggregate_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "aggregate-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::aggregate_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Aggregates,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Aggregates,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "typedef-inout", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (TupleTypedef2,)| + { + let host = caller; + let r = ::typedef_inout(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(i32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn tuple_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (char, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_arg(store, x) + } + fn tuple_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (char, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_result(store) + } + fn empty_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Empty, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::empty_arg(store, x) + } + fn empty_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Empty + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::empty_result(store) + } + fn scalar_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Scalars, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::scalar_arg(store, x) + } + fn scalar_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Scalars + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::scalar_result(store) + } + fn flags_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: ReallyFlags, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::flags_arg(store, x) + } + fn flags_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ReallyFlags + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::flags_result(store) + } + fn aggregate_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Aggregates, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::aggregate_arg(store, x) + } + fn aggregate_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Aggregates + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::aggregate_result(store) + } + fn typedef_inout( + store: wasmtime::StoreContextMut<'_, Self::Data>, + e: TupleTypedef2, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> i32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::typedef_inout(store, e) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod records { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Empty {} + impl core::fmt::Debug for Empty { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("Empty").finish() + } + } + const _: () = { + assert!( + 0 == < Empty as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Empty as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + /// A record containing two scalar fields + /// that both have the same type + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Scalars { + /// The first field, named a + #[component(name = "a")] + pub a: u32, + /// The second field, named b + #[component(name = "b")] + pub b: u32, + } + impl core::fmt::Debug for Scalars { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("Scalars") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } + } + const _: () = { + assert!( + 8 == < Scalars as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Scalars as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + /// A record that is really just flags + /// All of the fields are bool + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct ReallyFlags { + #[component(name = "a")] + pub a: bool, + #[component(name = "b")] + pub b: bool, + #[component(name = "c")] + pub c: bool, + #[component(name = "d")] + pub d: bool, + #[component(name = "e")] + pub e: bool, + #[component(name = "f")] + pub f: bool, + #[component(name = "g")] + pub g: bool, + #[component(name = "h")] + pub h: bool, + #[component(name = "i")] + pub i: bool, + } + impl core::fmt::Debug for ReallyFlags { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("ReallyFlags") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .field("e", &self.e) + .field("f", &self.f) + .field("g", &self.g) + .field("h", &self.h) + .field("i", &self.i) + .finish() + } + } + const _: () = { + assert!( + 9 == < ReallyFlags as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 1 == < ReallyFlags as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct Aggregates { + #[component(name = "a")] + pub a: Scalars, + #[component(name = "b")] + pub b: u32, + #[component(name = "c")] + pub c: Empty, + #[component(name = "d")] + pub d: wasmtime::component::__internal::String, + #[component(name = "e")] + pub e: ReallyFlags, + } + impl core::fmt::Debug for Aggregates { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("Aggregates") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .field("e", &self.e) + .finish() + } + } + const _: () = { + assert!( + 32 == < Aggregates as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < Aggregates as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub type IntTypedef = i32; + const _: () = { + assert!( + 4 == < IntTypedef as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < IntTypedef as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub type TupleTypedef2 = (IntTypedef,); + const _: () = { + assert!( + 4 == < TupleTypedef2 as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < TupleTypedef2 as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + pub struct Guest { + tuple_arg: wasmtime::component::Func, + tuple_result: wasmtime::component::Func, + empty_arg: wasmtime::component::Func, + empty_result: wasmtime::component::Func, + scalar_arg: wasmtime::component::Func, + scalar_result: wasmtime::component::Func, + flags_arg: wasmtime::component::Func, + flags_result: wasmtime::component::Func, + aggregate_arg: wasmtime::component::Func, + aggregate_result: wasmtime::component::Func, + typedef_inout: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + tuple_arg: wasmtime::component::ComponentExportIndex, + tuple_result: wasmtime::component::ComponentExportIndex, + empty_arg: wasmtime::component::ComponentExportIndex, + empty_result: wasmtime::component::ComponentExportIndex, + scalar_arg: wasmtime::component::ComponentExportIndex, + scalar_result: wasmtime::component::ComponentExportIndex, + flags_arg: wasmtime::component::ComponentExportIndex, + flags_result: wasmtime::component::ComponentExportIndex, + aggregate_arg: wasmtime::component::ComponentExportIndex, + aggregate_result: wasmtime::component::ComponentExportIndex, + typedef_inout: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/records") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/records`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/records") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/records`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/records` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let tuple_arg = lookup("tuple-arg")?; + let tuple_result = lookup("tuple-result")?; + let empty_arg = lookup("empty-arg")?; + let empty_result = lookup("empty-result")?; + let scalar_arg = lookup("scalar-arg")?; + let scalar_result = lookup("scalar-result")?; + let flags_arg = lookup("flags-arg")?; + let flags_result = lookup("flags-result")?; + let aggregate_arg = lookup("aggregate-arg")?; + let aggregate_result = lookup("aggregate-result")?; + let typedef_inout = lookup("typedef-inout")?; + Ok(GuestIndices { + tuple_arg, + tuple_result, + empty_arg, + empty_result, + scalar_arg, + scalar_result, + flags_arg, + flags_result, + aggregate_arg, + aggregate_result, + typedef_inout, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let tuple_arg = *_instance + .get_typed_func::< + ((char, u32),), + (), + >(&mut store, &self.tuple_arg)? + .func(); + let tuple_result = *_instance + .get_typed_func::< + (), + ((char, u32),), + >(&mut store, &self.tuple_result)? + .func(); + let empty_arg = *_instance + .get_typed_func::<(Empty,), ()>(&mut store, &self.empty_arg)? + .func(); + let empty_result = *_instance + .get_typed_func::< + (), + (Empty,), + >(&mut store, &self.empty_result)? + .func(); + let scalar_arg = *_instance + .get_typed_func::< + (Scalars,), + (), + >(&mut store, &self.scalar_arg)? + .func(); + let scalar_result = *_instance + .get_typed_func::< + (), + (Scalars,), + >(&mut store, &self.scalar_result)? + .func(); + let flags_arg = *_instance + .get_typed_func::< + (ReallyFlags,), + (), + >(&mut store, &self.flags_arg)? + .func(); + let flags_result = *_instance + .get_typed_func::< + (), + (ReallyFlags,), + >(&mut store, &self.flags_result)? + .func(); + let aggregate_arg = *_instance + .get_typed_func::< + (&Aggregates,), + (), + >(&mut store, &self.aggregate_arg)? + .func(); + let aggregate_result = *_instance + .get_typed_func::< + (), + (Aggregates,), + >(&mut store, &self.aggregate_result)? + .func(); + let typedef_inout = *_instance + .get_typed_func::< + (TupleTypedef2,), + (i32,), + >(&mut store, &self.typedef_inout)? + .func(); + Ok(Guest { + tuple_arg, + tuple_result, + empty_arg, + empty_result, + scalar_arg, + scalar_result, + flags_arg, + flags_result, + aggregate_arg, + aggregate_result, + typedef_inout, + }) + } + } + impl Guest { + pub async fn call_tuple_arg( + &self, + mut store: S, + arg0: (char, u32), + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ((char, u32),), + (), + >::new_unchecked(self.tuple_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_tuple_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ((char, u32),), + >::new_unchecked(self.tuple_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_empty_arg( + &self, + mut store: S, + arg0: Empty, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Empty,), + (), + >::new_unchecked(self.empty_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_empty_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Empty,), + >::new_unchecked(self.empty_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_scalar_arg( + &self, + mut store: S, + arg0: Scalars, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Scalars,), + (), + >::new_unchecked(self.scalar_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_scalar_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Scalars,), + >::new_unchecked(self.scalar_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_flags_arg( + &self, + mut store: S, + arg0: ReallyFlags, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (ReallyFlags,), + (), + >::new_unchecked(self.flags_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_flags_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (ReallyFlags,), + >::new_unchecked(self.flags_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_aggregate_arg( + &self, + mut store: S, + arg0: Aggregates, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Aggregates,), + (), + >::new_unchecked(self.aggregate_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_aggregate_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Aggregates,), + >::new_unchecked(self.aggregate_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_typedef_inout( + &self, + mut store: S, + arg0: TupleTypedef2, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (TupleTypedef2,), + (i32,), + >::new_unchecked(self.typedef_inout) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/records_tracing_async.rs b/crates/component-macro/tests/expanded/records_tracing_async.rs index 075b7dc57ae1..917083741019 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 5870b202254f..b282f6c98b34 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 467bb6509fb6..27784c2bb6f6 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_concurrent.rs b/crates/component-macro/tests/expanded/rename_concurrent.rs new file mode 100644 index 000000000000..30e2fbbfd87d --- /dev/null +++ b/crates/component-macro/tests/expanded/rename_concurrent.rs @@ -0,0 +1,329 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `neptune`. +/// +/// This structure is created through [`NeptunePre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Neptune`] as well. +pub struct NeptunePre { + instance_pre: wasmtime::component::InstancePre, + indices: NeptuneIndices, +} +impl Clone for NeptunePre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> NeptunePre<_T> { + /// Creates a new copy of `NeptunePre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = NeptuneIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Neptune`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `neptune`. +/// +/// This is an implementation detail of [`NeptunePre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Neptune`] as well. +#[derive(Clone)] +pub struct NeptuneIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `neptune`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Neptune::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`NeptunePre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`NeptunePre::instantiate_async`] to +/// create a [`Neptune`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Neptune::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`NeptuneIndices::new_instance`] followed +/// by [`NeptuneIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Neptune {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl NeptuneIndices { + /// Creates a new copy of `NeptuneIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(NeptuneIndices {}) + } + /// Creates a new instance of [`NeptuneIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Neptune`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Neptune`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(NeptuneIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Neptune`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Neptune {}) + } + } + impl Neptune { + /// Convenience wrapper around [`NeptunePre::new`] and + /// [`NeptunePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + NeptunePre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`NeptuneIndices::new_instance`] and + /// [`NeptuneIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = NeptuneIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::green::Host + foo::foo::red::Host + 'static, + U: Send + foo::foo::green::Host + foo::foo::red::Host, + { + foo::foo::green::add_to_linker(linker, get)?; + foo::foo::red::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod green { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Thing = i32; + const _: () = { + assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/green")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + #[allow(clippy::all)] + pub mod red { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Thing = super::super::super::foo::foo::green::Thing; + const _: () = { + assert!(4 == < Thing as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Thing as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Thing + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/red")?; + inst.func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Thing,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Thing,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Thing + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::foo(store) + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/rename_tracing_async.rs b/crates/component-macro/tests/expanded/rename_tracing_async.rs index 2adc67f4420c..e21aeb24189a 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 4414d8344ee2..97069b44b2c3 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, Box}; 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 9a37b5488e67..f0663b102421 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, Box}; 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_concurrent.rs b/crates/component-macro/tests/expanded/resources-export_concurrent.rs new file mode 100644 index 000000000000..7a4169e4ad14 --- /dev/null +++ b/crates/component-macro/tests/expanded/resources-export_concurrent.rs @@ -0,0 +1,935 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `w`. +/// +/// This structure is created through [`WPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`W`] as well. +pub struct WPre { + instance_pre: wasmtime::component::InstancePre, + indices: WIndices, +} +impl Clone for WPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> WPre<_T> { + /// Creates a new copy of `WPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = WIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`W`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `w`. +/// +/// This is an implementation detail of [`WPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`W`] as well. +#[derive(Clone)] +pub struct WIndices { + interface0: exports::foo::foo::simple_export::GuestIndices, + interface1: exports::foo::foo::export_using_import::GuestIndices, + interface2: exports::foo::foo::export_using_export1::GuestIndices, + interface3: exports::foo::foo::export_using_export2::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `w`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`W::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`WPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`WPre::instantiate_async`] to +/// create a [`W`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`W::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`WIndices::new_instance`] followed +/// by [`WIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct W { + interface0: exports::foo::foo::simple_export::Guest, + interface1: exports::foo::foo::export_using_import::Guest, + interface2: exports::foo::foo::export_using_export1::Guest, + interface3: exports::foo::foo::export_using_export2::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl WIndices { + /// Creates a new copy of `WIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::simple_export::GuestIndices::new( + _component, + )?; + let interface1 = exports::foo::foo::export_using_import::GuestIndices::new( + _component, + )?; + let interface2 = exports::foo::foo::export_using_export1::GuestIndices::new( + _component, + )?; + let interface3 = exports::foo::foo::export_using_export2::GuestIndices::new( + _component, + )?; + Ok(WIndices { + interface0, + interface1, + interface2, + interface3, + }) + } + /// Creates a new instance of [`WIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`W`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`W`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::simple_export::GuestIndices::new_instance( + &mut store, + _instance, + )?; + let interface1 = exports::foo::foo::export_using_import::GuestIndices::new_instance( + &mut store, + _instance, + )?; + let interface2 = exports::foo::foo::export_using_export1::GuestIndices::new_instance( + &mut store, + _instance, + )?; + let interface3 = exports::foo::foo::export_using_export2::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(WIndices { + interface0, + interface1, + interface2, + interface3, + }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`W`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + let interface1 = self.interface1.load(&mut store, &_instance)?; + let interface2 = self.interface2.load(&mut store, &_instance)?; + let interface3 = self.interface3.load(&mut store, &_instance)?; + Ok(W { + interface0, + interface1, + interface2, + interface3, + }) + } + } + impl W { + /// Convenience wrapper around [`WPre::new`] and + /// [`WPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + WPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`WIndices::new_instance`] and + /// [`WIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = WIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::transitive_import::Host + 'static, + U: Send + foo::foo::transitive_import::Host, + { + foo::foo::transitive_import::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_simple_export(&self) -> &exports::foo::foo::simple_export::Guest { + &self.interface0 + } + pub fn foo_foo_export_using_import( + &self, + ) -> &exports::foo::foo::export_using_import::Guest { + &self.interface1 + } + pub fn foo_foo_export_using_export1( + &self, + ) -> &exports::foo::foo::export_using_export1::Guest { + &self.interface2 + } + pub fn foo_foo_export_using_export2( + &self, + ) -> &exports::foo::foo::export_using_export2::Guest { + &self.interface3 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod transitive_import { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub enum Y {} + pub trait HostY: Sized { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; + } + impl<_T: HostY + ?Sized> HostY for &mut _T { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostY::drop(*self, rep) + } + } + pub trait Host: HostY + Sized {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/transitive-import")?; + inst.resource( + "y", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostY::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod simple_export { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = wasmtime::component::ResourceAny; + pub struct GuestA<'a> { + funcs: &'a Guest, + } + pub struct Guest { + constructor_a_constructor: wasmtime::component::Func, + static_a_static_a: wasmtime::component::Func, + method_a_method_a: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + constructor_a_constructor: wasmtime::component::ComponentExportIndex, + static_a_static_a: wasmtime::component::ComponentExportIndex, + method_a_method_a: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/simple-export") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple-export`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/simple-export") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple-export`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/simple-export` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let constructor_a_constructor = lookup("[constructor]a")?; + let static_a_static_a = lookup("[static]a.static-a")?; + let method_a_method_a = lookup("[method]a.method-a")?; + Ok(GuestIndices { + constructor_a_constructor, + static_a_static_a, + method_a_method_a, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let constructor_a_constructor = *_instance + .get_typed_func::< + (), + (wasmtime::component::ResourceAny,), + >(&mut store, &self.constructor_a_constructor)? + .func(); + let static_a_static_a = *_instance + .get_typed_func::< + (), + (u32,), + >(&mut store, &self.static_a_static_a)? + .func(); + let method_a_method_a = *_instance + .get_typed_func::< + (wasmtime::component::ResourceAny,), + (u32,), + >(&mut store, &self.method_a_method_a)? + .func(); + Ok(Guest { + constructor_a_constructor, + static_a_static_a, + method_a_method_a, + }) + } + } + impl Guest { + pub fn a(&self) -> GuestA<'_> { + GuestA { funcs: self } + } + } + impl GuestA<'_> { + pub async fn call_constructor( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::ResourceAny,), + >::new_unchecked(self.funcs.constructor_a_constructor) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_static_a( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32,), + >::new_unchecked(self.funcs.static_a_static_a) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_method_a( + &self, + mut store: S, + arg0: wasmtime::component::ResourceAny, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::ResourceAny,), + (u32,), + >::new_unchecked(self.funcs.method_a_method_a) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + #[allow(clippy::all)] + pub mod export_using_import { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Y = super::super::super::super::foo::foo::transitive_import::Y; + pub type A = wasmtime::component::ResourceAny; + pub struct GuestA<'a> { + funcs: &'a Guest, + } + pub struct Guest { + constructor_a_constructor: wasmtime::component::Func, + static_a_static_a: wasmtime::component::Func, + method_a_method_a: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + constructor_a_constructor: wasmtime::component::ComponentExportIndex, + static_a_static_a: wasmtime::component::ComponentExportIndex, + method_a_method_a: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/export-using-import") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-import`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/export-using-import") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-import`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/export-using-import` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let constructor_a_constructor = lookup("[constructor]a")?; + let static_a_static_a = lookup("[static]a.static-a")?; + let method_a_method_a = lookup("[method]a.method-a")?; + Ok(GuestIndices { + constructor_a_constructor, + static_a_static_a, + method_a_method_a, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let constructor_a_constructor = *_instance + .get_typed_func::< + (wasmtime::component::Resource,), + (wasmtime::component::ResourceAny,), + >(&mut store, &self.constructor_a_constructor)? + .func(); + let static_a_static_a = *_instance + .get_typed_func::< + (), + (wasmtime::component::Resource,), + >(&mut store, &self.static_a_static_a)? + .func(); + let method_a_method_a = *_instance + .get_typed_func::< + ( + wasmtime::component::ResourceAny, + wasmtime::component::Resource, + ), + (wasmtime::component::Resource,), + >(&mut store, &self.method_a_method_a)? + .func(); + Ok(Guest { + constructor_a_constructor, + static_a_static_a, + method_a_method_a, + }) + } + } + impl Guest { + pub fn a(&self) -> GuestA<'_> { + GuestA { funcs: self } + } + } + impl GuestA<'_> { + pub async fn call_constructor( + &self, + mut store: S, + arg0: wasmtime::component::Resource, + ) -> wasmtime::Result< + wasmtime::component::Promise, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::Resource,), + (wasmtime::component::ResourceAny,), + >::new_unchecked(self.funcs.constructor_a_constructor) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_static_a( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::Resource,), + >::new_unchecked(self.funcs.static_a_static_a) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_method_a( + &self, + mut store: S, + arg0: wasmtime::component::ResourceAny, + arg1: wasmtime::component::Resource, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::ResourceAny, + wasmtime::component::Resource, + ), + (wasmtime::component::Resource,), + >::new_unchecked(self.funcs.method_a_method_a) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0, arg1)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + #[allow(clippy::all)] + pub mod export_using_export1 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = wasmtime::component::ResourceAny; + pub struct GuestA<'a> { + funcs: &'a Guest, + } + pub struct Guest { + constructor_a_constructor: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + constructor_a_constructor: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/export-using-export1") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-export1`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/export-using-export1") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-export1`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/export-using-export1` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let constructor_a_constructor = lookup("[constructor]a")?; + Ok(GuestIndices { + constructor_a_constructor, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let constructor_a_constructor = *_instance + .get_typed_func::< + (), + (wasmtime::component::ResourceAny,), + >(&mut store, &self.constructor_a_constructor)? + .func(); + Ok(Guest { constructor_a_constructor }) + } + } + impl Guest { + pub fn a(&self) -> GuestA<'_> { + GuestA { funcs: self } + } + } + impl GuestA<'_> { + pub async fn call_constructor( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::ResourceAny,), + >::new_unchecked(self.funcs.constructor_a_constructor) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + #[allow(clippy::all)] + pub mod export_using_export2 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = super::super::super::super::exports::foo::foo::export_using_export1::A; + pub type B = wasmtime::component::ResourceAny; + pub struct GuestB<'a> { + funcs: &'a Guest, + } + pub struct Guest { + constructor_b_constructor: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + constructor_b_constructor: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/export-using-export2") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-export2`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/export-using-export2") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/export-using-export2`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/export-using-export2` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let constructor_b_constructor = lookup("[constructor]b")?; + Ok(GuestIndices { + constructor_b_constructor, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let constructor_b_constructor = *_instance + .get_typed_func::< + (wasmtime::component::ResourceAny,), + (wasmtime::component::ResourceAny,), + >(&mut store, &self.constructor_b_constructor)? + .func(); + Ok(Guest { constructor_b_constructor }) + } + } + impl Guest { + pub fn b(&self) -> GuestB<'_> { + GuestB { funcs: self } + } + } + impl GuestB<'_> { + pub async fn call_constructor( + &self, + mut store: S, + arg0: wasmtime::component::ResourceAny, + ) -> wasmtime::Result< + wasmtime::component::Promise, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::ResourceAny,), + (wasmtime::component::ResourceAny,), + >::new_unchecked(self.funcs.constructor_b_constructor) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} 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 7cf0ade0f577..c2ea31fb3b8c 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, Box}; 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 cd27d777f581..5f713a84a72c 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. /// @@ -130,10 +130,11 @@ pub trait TheWorldImports: HostWorldResource { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -229,7 +230,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,9 +246,12 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut linker = linker.root(); linker @@ -321,7 +328,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 +356,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; 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 +440,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 +502,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 +876,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum A {} - pub trait HostA { + pub trait HostA: Sized { fn drop( &mut self, rep: wasmtime::component::Resource, @@ -876,22 +890,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 +943,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 +983,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 +1025,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 +1074,7 @@ pub mod foo { #[allow(unused_imports)] use wasmtime::component::__internal::{anyhow, Box}; pub enum Foo {} - pub trait HostFoo { + pub trait HostFoo: Sized { fn drop( &mut self, rep: wasmtime::component::Resource, @@ -1058,22 +1088,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 +1233,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 99899d3254fc..5d1ec7a22ce0 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) @@ -135,10 +132,11 @@ pub trait TheWorldImports: Send + HostWorldResource { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -236,7 +234,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,9 +248,12 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -347,7 +348,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { let callee = unsafe { wasmtime::component::TypedFunc::< @@ -374,7 +375,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +463,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 +530,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 +961,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +976,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 +1037,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 +1082,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 +1129,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 +1185,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +1200,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 +1354,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_concurrent.rs b/crates/component-macro/tests/expanded/resources-import_concurrent.rs new file mode 100644 index 000000000000..aa4acded54f6 --- /dev/null +++ b/crates/component-macro/tests/expanded/resources-import_concurrent.rs @@ -0,0 +1,2383 @@ +pub enum WorldResource {} +pub trait HostWorldResource: Sized { + type WorldResourceData; + fn new( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn static_foo( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; +} +impl<_T: HostWorldResource> HostWorldResource for &mut _T { + type WorldResourceData = _T::WorldResourceData; + fn new( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostWorldResource>::new(store) + } + fn foo( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostWorldResource>::foo(store, self_) + } + fn static_foo( + store: wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::WorldResourceData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostWorldResource>::static_foo(store) + } + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostWorldResource::drop(*self, rep) + } +} +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface1: exports::foo::foo::uses_resource_transitively::GuestIndices, + some_world_func2: wasmtime::component::ComponentExportIndex, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface1: exports::foo::foo::uses_resource_transitively::Guest, + some_world_func2: wasmtime::component::Func, +} +pub trait TheWorldImports: HostWorldResource { + type Data; + fn some_world_func( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; +} +pub trait TheWorldImportsGetHost< + T, + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: TheWorldImports; +} +impl TheWorldImportsGetHost for F +where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: TheWorldImports, +{ + type Host = O; +} +impl<_T: TheWorldImports> TheWorldImports for &mut _T { + type Data = _T::Data; + fn some_world_func( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as TheWorldImports>::some_world_func(store) + } +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface1 = exports::foo::foo::uses_resource_transitively::GuestIndices::new( + _component, + )?; + let some_world_func2 = _component + .export_index(None, "some-world-func2") + .ok_or_else(|| { + anyhow::anyhow!("no function export `some-world-func2` found") + })? + .1; + Ok(TheWorldIndices { + interface1, + some_world_func2, + }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface1 = exports::foo::foo::uses_resource_transitively::GuestIndices::new_instance( + &mut store, + _instance, + )?; + let some_world_func2 = _instance + .get_export(&mut store, None, "some-world-func2") + .ok_or_else(|| { + anyhow::anyhow!("no function export `some-world-func2` found") + })?; + Ok(TheWorldIndices { + interface1, + some_world_func2, + }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface1 = self.interface1.load(&mut store, &_instance)?; + let some_world_func2 = *_instance + .get_typed_func::< + (), + (wasmtime::component::Resource,), + >(&mut store, &self.some_world_func2)? + .func(); + Ok(TheWorld { + interface1, + some_world_func2, + }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost< + &'a mut T, + T, + Host: TheWorldImports, + >, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut linker = linker.root(); + linker + .resource( + "world-resource", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostWorldResource::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + linker + .func_wrap_concurrent( + "[constructor]world-resource", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::new(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + linker + .func_wrap_concurrent( + "[method]world-resource.foo", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::foo(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + linker + .func_wrap_concurrent( + "[static]world-resource.static-foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::static_foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + linker + .func_wrap_concurrent( + "some-world-func", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::some_world_func(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::resources::Host + + foo::foo::long_use_chain1::Host + foo::foo::long_use_chain2::Host + + foo::foo::long_use_chain3::Host + + foo::foo::long_use_chain4::Host + + foo::foo::transitive_interface_with_resource::Host + + TheWorldImports + 'static, + U: Send + foo::foo::resources::Host + + foo::foo::long_use_chain1::Host + foo::foo::long_use_chain2::Host + + foo::foo::long_use_chain3::Host + + foo::foo::long_use_chain4::Host + + foo::foo::transitive_interface_with_resource::Host + + TheWorldImports, + { + Self::add_to_linker_imports_get_host(linker, get)?; + foo::foo::resources::add_to_linker(linker, get)?; + foo::foo::long_use_chain1::add_to_linker(linker, get)?; + foo::foo::long_use_chain2::add_to_linker(linker, get)?; + foo::foo::long_use_chain3::add_to_linker(linker, get)?; + foo::foo::long_use_chain4::add_to_linker(linker, get)?; + foo::foo::transitive_interface_with_resource::add_to_linker(linker, get)?; + Ok(()) + } + pub async fn call_some_world_func2( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::Resource,), + >::new_unchecked(self.some_world_func2) + }; + let promise = callee.call_concurrent(store.as_context_mut(), ()).await?; + Ok(promise.map(|(v,)| v)) + } + pub fn foo_foo_uses_resource_transitively( + &self, + ) -> &exports::foo::foo::uses_resource_transitively::Guest { + &self.interface1 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod resources { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub enum Bar {} + pub trait HostBar: Sized { + type BarData; + fn new( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn static_a( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn method_a( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; + } + impl<_T: HostBar> HostBar for &mut _T { + type BarData = _T::BarData; + fn new( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostBar>::new(store) + } + fn static_a( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostBar>::static_a(store) + } + fn method_a( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostBar>::method_a(store, self_) + } + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostBar::drop(*self, rep) + } + } + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct NestedOwn { + #[component(name = "nested-bar")] + pub nested_bar: wasmtime::component::Resource, + } + impl core::fmt::Debug for NestedOwn { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("NestedOwn") + .field("nested-bar", &self.nested_bar) + .finish() + } + } + const _: () = { + assert!( + 4 == < NestedOwn as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < NestedOwn as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct NestedBorrow { + #[component(name = "nested-bar")] + pub nested_bar: wasmtime::component::Resource, + } + impl core::fmt::Debug for NestedBorrow { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("NestedBorrow") + .field("nested-bar", &self.nested_bar) + .finish() + } + } + const _: () = { + assert!( + 4 == < NestedBorrow as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < NestedBorrow as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub type SomeHandle = wasmtime::component::Resource; + const _: () = { + assert!( + 4 == < SomeHandle as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < SomeHandle as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub trait Host: HostBar + Sized { + type Data; + fn bar_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn bar_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn bar_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (wasmtime::component::Resource, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (wasmtime::component::Resource, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn tuple_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + wasmtime::component::Resource, + u32, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn option_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn option_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn option_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option< + wasmtime::component::Resource, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Result, ()>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Result, ()>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result< + wasmtime::component::Resource, + (), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn list_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn record_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: NestedOwn, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn record_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: NestedBorrow, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn record_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> NestedOwn + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn func_with_handle_typedef( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: SomeHandle, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost< + &'a mut T, + T, + Host: Host + Send, + >, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/resources")?; + inst.resource( + "bar", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostBar::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + inst.func_wrap_concurrent( + "[constructor]bar", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::new(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "[static]bar.static-a", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::static_a(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "[method]bar.method-a", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::method_a(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bar-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::bar_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bar-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::bar_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bar-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::bar_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): ((wasmtime::component::Resource, u32),)| + { + let host = caller; + let r = ::tuple_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): ((wasmtime::component::Resource, u32),)| + { + let host = caller; + let r = ::tuple_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "tuple-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::tuple_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ((wasmtime::component::Resource, u32),), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ((wasmtime::component::Resource, u32),), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "option-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Option>,)| + { + let host = caller; + let r = ::option_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "option-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Option>,)| + { + let host = caller; + let r = ::option_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "option-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::option_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Option>,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Option>,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Result, ()>,)| + { + let host = caller; + let r = ::result_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (Result, ()>,)| + { + let host = caller; + let r = ::result_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::result_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result, ()>,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result, ()>,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + )| + { + let host = caller; + let r = ::list_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + )| + { + let host = caller; + let r = ::list_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "list-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::list_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "record-own-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (NestedOwn,)| + { + let host = caller; + let r = ::record_own_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "record-borrow-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (NestedBorrow,)| + { + let host = caller; + let r = ::record_borrow_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "record-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::record_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(NestedOwn,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(NestedOwn,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "func-with-handle-typedef", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (SomeHandle,)| + { + let host = caller; + let r = ::func_with_handle_typedef(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn bar_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bar_own_arg(store, x) + } + fn bar_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bar_borrow_arg(store, x) + } + fn bar_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bar_result(store) + } + fn tuple_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (wasmtime::component::Resource, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_own_arg(store, x) + } + fn tuple_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: (wasmtime::component::Resource, u32), + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_borrow_arg(store, x) + } + fn tuple_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + wasmtime::component::Resource, + u32, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::tuple_result(store) + } + fn option_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_own_arg(store, x) + } + fn option_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_borrow_arg(store, x) + } + fn option_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option< + wasmtime::component::Resource, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_result(store) + } + fn result_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Result, ()>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_own_arg(store, x) + } + fn result_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: Result, ()>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_borrow_arg(store, x) + } + fn result_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result< + wasmtime::component::Resource, + (), + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_result(store) + } + fn list_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_own_arg(store, x) + } + fn list_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_borrow_arg(store, x) + } + fn list_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::Resource, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::list_result(store) + } + fn record_own_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: NestedOwn, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::record_own_arg(store, x) + } + fn record_borrow_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: NestedBorrow, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::record_borrow_arg(store, x) + } + fn record_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> NestedOwn + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::record_result(store) + } + fn func_with_handle_typedef( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: SomeHandle, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::func_with_handle_typedef(store, x) + } + } + } + #[allow(clippy::all)] + pub mod long_use_chain1 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub enum A {} + pub trait HostA: Sized { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; + } + impl<_T: HostA + ?Sized> HostA for &mut _T { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostA::drop(*self, rep) + } + } + pub trait Host: HostA + Sized {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/long-use-chain1")?; + inst.resource( + "a", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostA::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + #[allow(clippy::all)] + pub mod long_use_chain2 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = super::super::super::foo::foo::long_use_chain1::A; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/long-use-chain2")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + #[allow(clippy::all)] + pub mod long_use_chain3 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = super::super::super::foo::foo::long_use_chain2::A; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/long-use-chain3")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + #[allow(clippy::all)] + pub mod long_use_chain4 { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type A = super::super::super::foo::foo::long_use_chain3::A; + pub trait Host { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/long-use-chain4")?; + inst.func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::Resource,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::Resource + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::foo(store) + } + } + } + #[allow(clippy::all)] + pub mod transitive_interface_with_resource { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub enum Foo {} + pub trait HostFoo: Sized { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; + } + impl<_T: HostFoo + ?Sized> HostFoo for &mut _T { + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostFoo::drop(*self, rep) + } + } + pub trait Host: HostFoo + Sized {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker + .instance("foo:foo/transitive-interface-with-resource")?; + inst.resource( + "foo", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostFoo::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod uses_resource_transitively { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Foo = super::super::super::super::foo::foo::transitive_interface_with_resource::Foo; + pub struct Guest { + handle: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + handle: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/uses-resource-transitively") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/uses-resource-transitively`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export( + &mut store, + None, + "foo:foo/uses-resource-transitively", + ) + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/uses-resource-transitively`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/uses-resource-transitively` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let handle = lookup("handle")?; + Ok(GuestIndices { handle }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let handle = *_instance + .get_typed_func::< + (wasmtime::component::Resource,), + (), + >(&mut store, &self.handle)? + .func(); + Ok(Guest { handle }) + } + } + impl Guest { + pub async fn call_handle( + &self, + mut store: S, + arg0: wasmtime::component::Resource, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::Resource,), + (), + >::new_unchecked(self.handle) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + } + } + } + } +} 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 5332127243bc..c78658a17a27 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) @@ -135,10 +132,11 @@ pub trait TheWorldImports: Send + HostWorldResource { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -236,7 +234,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,9 +248,12 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -402,7 +403,7 @@ const _: () = { mut store: S, ) -> wasmtime::Result> where - ::Data: Send, + ::Data: Send + 'static, { use tracing::Instrument; let span = tracing::span!( @@ -437,7 +438,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +526,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 +593,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 +1352,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +1367,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 +1428,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 +1473,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 +1520,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 +1589,7 @@ pub mod foo { use wasmtime::component::__internal::{anyhow, Box}; 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 +1604,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 +1758,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 7d3d2b33c093..a022b205c29c 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 b6f3d4e9b6b5..1d44846fd545 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_concurrent.rs b/crates/component-macro/tests/expanded/share-types_concurrent.rs new file mode 100644 index 000000000000..3c7422d8f650 --- /dev/null +++ b/crates/component-macro/tests/expanded/share-types_concurrent.rs @@ -0,0 +1,496 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `http-interface`. +/// +/// This structure is created through [`HttpInterfacePre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`HttpInterface`] as well. +pub struct HttpInterfacePre { + instance_pre: wasmtime::component::InstancePre, + indices: HttpInterfaceIndices, +} +impl Clone for HttpInterfacePre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> HttpInterfacePre<_T> { + /// Creates a new copy of `HttpInterfacePre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = HttpInterfaceIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`HttpInterface`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `http-interface`. +/// +/// This is an implementation detail of [`HttpInterfacePre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`HttpInterface`] as well. +#[derive(Clone)] +pub struct HttpInterfaceIndices { + interface0: exports::http_handler::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `http-interface`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`HttpInterface::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`HttpInterfacePre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`HttpInterfacePre::instantiate_async`] to +/// create a [`HttpInterface`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`HttpInterface::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`HttpInterfaceIndices::new_instance`] followed +/// by [`HttpInterfaceIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct HttpInterface { + interface0: exports::http_handler::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl HttpInterfaceIndices { + /// Creates a new copy of `HttpInterfaceIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::http_handler::GuestIndices::new(_component)?; + Ok(HttpInterfaceIndices { interface0 }) + } + /// Creates a new instance of [`HttpInterfaceIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`HttpInterface`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`HttpInterface`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::http_handler::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(HttpInterfaceIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`HttpInterface`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(HttpInterface { interface0 }) + } + } + impl HttpInterface { + /// Convenience wrapper around [`HttpInterfacePre::new`] and + /// [`HttpInterfacePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + HttpInterfacePre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`HttpInterfaceIndices::new_instance`] and + /// [`HttpInterfaceIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = HttpInterfaceIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::http_types::Host + http_fetch::Host + 'static, + U: Send + foo::foo::http_types::Host + http_fetch::Host, + { + foo::foo::http_types::add_to_linker(linker, get)?; + http_fetch::add_to_linker(linker, get)?; + Ok(()) + } + pub fn http_handler(&self) -> &exports::http_handler::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod http_types { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct Request { + #[component(name = "method")] + pub method: wasmtime::component::__internal::String, + } + impl core::fmt::Debug for Request { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Request").field("method", &self.method).finish() + } + } + const _: () = { + assert!(8 == < Request as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Request as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct Response { + #[component(name = "body")] + pub body: wasmtime::component::__internal::String, + } + impl core::fmt::Debug for Response { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Response").field("body", &self.body).finish() + } + } + const _: () = { + assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); + assert!( + 4 == < Response as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/http-types")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} +#[allow(clippy::all)] +pub mod http_fetch { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Request = super::foo::foo::http_types::Request; + const _: () = { + assert!(8 == < Request as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Request as wasmtime::component::ComponentType >::ALIGN32); + }; + pub type Response = super::foo::foo::http_types::Response; + const _: () = { + assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn fetch_request( + store: wasmtime::StoreContextMut<'_, Self::Data>, + request: Request, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Response + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("http-fetch")?; + inst.func_wrap_concurrent( + "fetch-request", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (Request,)| { + let host = caller; + let r = ::fetch_request(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Response,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Response,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn fetch_request( + store: wasmtime::StoreContextMut<'_, Self::Data>, + request: Request, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Response + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::fetch_request(store, request) + } + } +} +pub mod exports { + #[allow(clippy::all)] + pub mod http_handler { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Request = super::super::foo::foo::http_types::Request; + const _: () = { + assert!(8 == < Request as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Request as wasmtime::component::ComponentType >::ALIGN32); + }; + pub type Response = super::super::foo::foo::http_types::Response; + const _: () = { + assert!(8 == < Response as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Response as wasmtime::component::ComponentType >::ALIGN32); + }; + pub struct Guest { + handle_request: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + handle_request: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "http-handler") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `http-handler`") + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "http-handler") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `http-handler`") + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `http-handler` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let handle_request = lookup("handle-request")?; + Ok(GuestIndices { handle_request }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let handle_request = *_instance + .get_typed_func::< + (&Request,), + (Response,), + >(&mut store, &self.handle_request)? + .func(); + Ok(Guest { handle_request }) + } + } + impl Guest { + pub async fn call_handle_request( + &self, + mut store: S, + arg0: Request, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Request,), + (Response,), + >::new_unchecked(self.handle_request) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } +} 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 be08b82994fa..dba31158ccc7 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 9b4a809d9713..2e1356cf8eed 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 8f564e80ae1d..b2bee3922df6 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_concurrent.rs b/crates/component-macro/tests/expanded/simple-functions_concurrent.rs new file mode 100644 index 000000000000..b626be4e210f --- /dev/null +++ b/crates/component-macro/tests/expanded/simple-functions_concurrent.rs @@ -0,0 +1,805 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::simple::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::simple::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::simple::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::simple::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::simple::Host + 'static, + U: Send + foo::foo::simple::Host, + { + foo::foo::simple::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_simple(&self) -> &exports::foo::foo::simple::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod simple { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn f1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + b: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn f6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + b: u32, + c: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, u32, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/simple")?; + inst.func_wrap_concurrent( + "f1", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f1(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (u32,)| { + let host = caller; + let r = ::f2(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f3", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0, arg1): (u32, u32)| + { + let host = caller; + let r = ::f3(host, arg0, arg1); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f4", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f4(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(u32,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f5", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::f5(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((u32, u32),)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((u32, u32),)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "f6", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0, arg1, arg2): (u32, u32, u32)| + { + let host = caller; + let r = ::f6(host, arg0, arg1, arg2); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((u32, u32, u32),)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<((u32, u32, u32),)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn f1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f1(store) + } + fn f2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f2(store, a) + } + fn f3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + b: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f3(store, a, b) + } + fn f4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> u32 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f4(store) + } + fn f5( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f5(store) + } + fn f6( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: u32, + b: u32, + c: u32, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> (u32, u32, u32) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::f6(store, a, b, c) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod simple { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + f1: wasmtime::component::Func, + f2: wasmtime::component::Func, + f3: wasmtime::component::Func, + f4: wasmtime::component::Func, + f5: wasmtime::component::Func, + f6: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + f1: wasmtime::component::ComponentExportIndex, + f2: wasmtime::component::ComponentExportIndex, + f3: wasmtime::component::ComponentExportIndex, + f4: wasmtime::component::ComponentExportIndex, + f5: wasmtime::component::ComponentExportIndex, + f6: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/simple") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/simple") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/simple` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let f1 = lookup("f1")?; + let f2 = lookup("f2")?; + let f3 = lookup("f3")?; + let f4 = lookup("f4")?; + let f5 = lookup("f5")?; + let f6 = lookup("f6")?; + Ok(GuestIndices { + f1, + f2, + f3, + f4, + f5, + f6, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let f1 = *_instance + .get_typed_func::<(), ()>(&mut store, &self.f1)? + .func(); + let f2 = *_instance + .get_typed_func::<(u32,), ()>(&mut store, &self.f2)? + .func(); + let f3 = *_instance + .get_typed_func::<(u32, u32), ()>(&mut store, &self.f3)? + .func(); + let f4 = *_instance + .get_typed_func::<(), (u32,)>(&mut store, &self.f4)? + .func(); + let f5 = *_instance + .get_typed_func::<(), ((u32, u32),)>(&mut store, &self.f5)? + .func(); + let f6 = *_instance + .get_typed_func::< + (u32, u32, u32), + ((u32, u32, u32),), + >(&mut store, &self.f6)? + .func(); + Ok(Guest { f1, f2, f3, f4, f5, f6 }) + } + } + impl Guest { + pub async fn call_f1( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (), + >::new_unchecked(self.f1) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise) + } + pub async fn call_f2( + &self, + mut store: S, + arg0: u32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u32,), + (), + >::new_unchecked(self.f2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_f3( + &self, + mut store: S, + arg0: u32, + arg1: u32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u32, u32), + (), + >::new_unchecked(self.f3) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0, arg1)) + .await?; + Ok(promise) + } + pub async fn call_f4( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (u32,), + >::new_unchecked(self.f4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_f5( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ((u32, u32),), + >::new_unchecked(self.f5) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_f6( + &self, + mut store: S, + arg0: u32, + arg1: u32, + arg2: u32, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (u32, u32, u32), + ((u32, u32, u32),), + >::new_unchecked(self.f6) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0, arg1, arg2)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} 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 d9771b87beae..a8d7b489b1e8 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 ddd44d6cca17..f9b1d04c4a72 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 d1ddd48e69dd..644c654edc17 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_concurrent.rs b/crates/component-macro/tests/expanded/simple-lists_concurrent.rs new file mode 100644 index 000000000000..3fc2a19dd107 --- /dev/null +++ b/crates/component-macro/tests/expanded/simple-lists_concurrent.rs @@ -0,0 +1,770 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `my-world`. +/// +/// This structure is created through [`MyWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`MyWorld`] as well. +pub struct MyWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: MyWorldIndices, +} +impl Clone for MyWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Creates a new copy of `MyWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = MyWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`MyWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `my-world`. +/// +/// This is an implementation detail of [`MyWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`MyWorld`] as well. +#[derive(Clone)] +pub struct MyWorldIndices { + interface0: exports::foo::foo::simple_lists::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `my-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`MyWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`MyWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`MyWorldPre::instantiate_async`] to +/// create a [`MyWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`MyWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`MyWorldIndices::new_instance`] followed +/// by [`MyWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct MyWorld { + interface0: exports::foo::foo::simple_lists::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl MyWorldIndices { + /// Creates a new copy of `MyWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::simple_lists::GuestIndices::new( + _component, + )?; + Ok(MyWorldIndices { interface0 }) + } + /// Creates a new instance of [`MyWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`MyWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`MyWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::simple_lists::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(MyWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`MyWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(MyWorld { interface0 }) + } + } + impl MyWorld { + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`MyWorldIndices::new_instance`] and + /// [`MyWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = MyWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::simple_lists::Host + 'static, + U: Send + foo::foo::simple_lists::Host, + { + foo::foo::simple_lists::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_simple_lists(&self) -> &exports::foo::foo::simple_lists::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod simple_lists { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn simple_list1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + l: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn simple_list2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn simple_list3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::Vec, + b: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn simple_list4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + l: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/simple-lists")?; + inst.func_wrap_concurrent( + "simple-list1", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::Vec,)| + { + let host = caller; + let r = ::simple_list1(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "simple-list2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::simple_list2(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::Vec,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "simple-list3", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + ): ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + )| + { + let host = caller; + let r = ::simple_list3(host, arg0, arg1); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "simple-list4", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + ): ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + )| + { + let host = caller; + let r = ::simple_list4(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn simple_list1( + store: wasmtime::StoreContextMut<'_, Self::Data>, + l: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::simple_list1(store, l) + } + fn simple_list2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + u32, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::simple_list2(store) + } + fn simple_list3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::Vec, + b: wasmtime::component::__internal::Vec, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::simple_list3(store, a, b) + } + fn simple_list4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + l: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::simple_list4(store, l) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod simple_lists { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + simple_list1: wasmtime::component::Func, + simple_list2: wasmtime::component::Func, + simple_list3: wasmtime::component::Func, + simple_list4: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + simple_list1: wasmtime::component::ComponentExportIndex, + simple_list2: wasmtime::component::ComponentExportIndex, + simple_list3: wasmtime::component::ComponentExportIndex, + simple_list4: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/simple-lists") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple-lists`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/simple-lists") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/simple-lists`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/simple-lists` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let simple_list1 = lookup("simple-list1")?; + let simple_list2 = lookup("simple-list2")?; + let simple_list3 = lookup("simple-list3")?; + let simple_list4 = lookup("simple-list4")?; + Ok(GuestIndices { + simple_list1, + simple_list2, + simple_list3, + simple_list4, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let simple_list1 = *_instance + .get_typed_func::< + (&[u32],), + (), + >(&mut store, &self.simple_list1)? + .func(); + let simple_list2 = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::Vec,), + >(&mut store, &self.simple_list2)? + .func(); + let simple_list3 = *_instance + .get_typed_func::< + (&[u32], &[u32]), + ( + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + ), + >(&mut store, &self.simple_list3)? + .func(); + let simple_list4 = *_instance + .get_typed_func::< + (&[wasmtime::component::__internal::Vec],), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ), + >(&mut store, &self.simple_list4)? + .func(); + Ok(Guest { + simple_list1, + simple_list2, + simple_list3, + simple_list4, + }) + } + } + impl Guest { + pub async fn call_simple_list1( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::Vec,), + (), + >::new_unchecked(self.simple_list1) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_simple_list2( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::Vec,), + >::new_unchecked(self.simple_list2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_simple_list3( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec, + arg1: wasmtime::component::__internal::Vec, + ) -> wasmtime::Result< + wasmtime::component::Promise< + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + ( + ( + wasmtime::component::__internal::Vec, + wasmtime::component::__internal::Vec, + ), + ), + >::new_unchecked(self.simple_list3) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0, arg1)) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_simple_list4( + &self, + mut store: S, + arg0: wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ), + ( + wasmtime::component::__internal::Vec< + wasmtime::component::__internal::Vec, + >, + ), + >::new_unchecked(self.simple_list4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} 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 5170ff1563d6..1997ff660cca 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 2feddc5520e8..7c7b66ee9179 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 5b95ff182556..64e82916bedd 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_concurrent.rs b/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs new file mode 100644 index 000000000000..078557cfb5c5 --- /dev/null +++ b/crates/component-macro/tests/expanded/simple-wasi_concurrent.rs @@ -0,0 +1,437 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `wasi`. +/// +/// This structure is created through [`WasiPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Wasi`] as well. +pub struct WasiPre { + instance_pre: wasmtime::component::InstancePre, + indices: WasiIndices, +} +impl Clone for WasiPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> WasiPre<_T> { + /// Creates a new copy of `WasiPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = WasiIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Wasi`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `wasi`. +/// +/// This is an implementation detail of [`WasiPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Wasi`] as well. +#[derive(Clone)] +pub struct WasiIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `wasi`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Wasi::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`WasiPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`WasiPre::instantiate_async`] to +/// create a [`Wasi`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Wasi::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`WasiIndices::new_instance`] followed +/// by [`WasiIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Wasi {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl WasiIndices { + /// Creates a new copy of `WasiIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(WasiIndices {}) + } + /// Creates a new instance of [`WasiIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Wasi`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Wasi`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(WasiIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Wasi`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Wasi {}) + } + } + impl Wasi { + /// Convenience wrapper around [`WasiPre::new`] and + /// [`WasiPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + WasiPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`WasiIndices::new_instance`] and + /// [`WasiIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = WasiIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::wasi_filesystem::Host + + foo::foo::wall_clock::Host + 'static, + U: Send + foo::foo::wasi_filesystem::Host + + foo::foo::wall_clock::Host, + { + foo::foo::wasi_filesystem::add_to_linker(linker, get)?; + foo::foo::wall_clock::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod wasi_filesystem { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct DescriptorStat {} + impl core::fmt::Debug for DescriptorStat { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DescriptorStat").finish() + } + } + const _: () = { + assert!( + 0 == < DescriptorStat as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < DescriptorStat as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum Errno { + #[component(name = "e")] + E, + } + impl Errno { + pub fn name(&self) -> &'static str { + match self { + Errno::E => "e", + } + } + pub fn message(&self) -> &'static str { + match self { + Errno::E => "", + } + } + } + impl core::fmt::Debug for Errno { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Errno") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl core::fmt::Display for Errno { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + impl std::error::Error for Errno {} + const _: () = { + assert!(1 == < Errno as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Errno as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn create_directory_at( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), Errno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn stat( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/wasi-filesystem")?; + inst.func_wrap_concurrent( + "create-directory-at", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::create_directory_at(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), Errno>,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), Errno>,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "stat", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::stat(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn create_directory_at( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), Errno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::create_directory_at(store) + } + fn stat( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::stat(store) + } + } + } + #[allow(clippy::all)] + pub mod wall_clock { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/wall-clock")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} 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 17b68e5c2c0e..23c5e20e2aec 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 f14766767851..cac6c83d8d6f 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 239bca6be1e2..ab20a2795d75 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_concurrent.rs b/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs new file mode 100644 index 000000000000..fed945684a7d --- /dev/null +++ b/crates/component-macro/tests/expanded/small-anonymous_concurrent.rs @@ -0,0 +1,532 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::anon::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::anon::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::anon::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::anon::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::anon::Host + 'static, + U: Send + foo::foo::anon::Host, + { + foo::foo::anon::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_anon(&self) -> &exports::foo::foo::anon::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod anon { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum Error { + #[component(name = "success")] + Success, + #[component(name = "failure")] + Failure, + } + impl Error { + pub fn name(&self) -> &'static str { + match self { + Error::Success => "success", + Error::Failure => "failure", + } + } + pub fn message(&self) -> &'static str { + match self { + Error::Success => "", + Error::Failure => "", + } + } + } + impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Error") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + impl std::error::Error for Error {} + const _: () = { + assert!(1 == < Error as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Error as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn option_test( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result< + Option, + Error, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/anon")?; + inst.func_wrap_concurrent( + "option-test", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::option_test(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + Result< + Option, + Error, + >, + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + Result< + Option, + Error, + >, + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn option_test( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result< + Option, + Error, + > + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_test(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod anon { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum Error { + #[component(name = "success")] + Success, + #[component(name = "failure")] + Failure, + } + impl Error { + pub fn name(&self) -> &'static str { + match self { + Error::Success => "success", + Error::Failure => "failure", + } + } + pub fn message(&self) -> &'static str { + match self { + Error::Success => "", + Error::Failure => "", + } + } + } + impl core::fmt::Debug for Error { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("Error") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl core::fmt::Display for Error { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + impl std::error::Error for Error {} + const _: () = { + assert!( + 1 == < Error as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Error as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub struct Guest { + option_test: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + option_test: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/anon") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `foo:foo/anon`") + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/anon") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `foo:foo/anon`") + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/anon` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let option_test = lookup("option-test")?; + Ok(GuestIndices { option_test }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let option_test = *_instance + .get_typed_func::< + (), + ( + Result< + Option, + Error, + >, + ), + >(&mut store, &self.option_test)? + .func(); + Ok(Guest { option_test }) + } + } + impl Guest { + pub async fn call_option_test( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + Result< + Option, + Error, + >, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ( + Result< + Option, + Error, + >, + ), + >::new_unchecked(self.option_test) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} 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 1226081d5fad..a708df1e2e0e 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_concurrent.rs b/crates/component-macro/tests/expanded/smoke-default_concurrent.rs new file mode 100644 index 000000000000..ed33f75c331c --- /dev/null +++ b/crates/component-macro/tests/expanded/smoke-default_concurrent.rs @@ -0,0 +1,187 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + y: wasmtime::component::ComponentExportIndex, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + y: wasmtime::component::Func, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let y = _component + .export_index(None, "y") + .ok_or_else(|| anyhow::anyhow!("no function export `y` found"))? + .1; + Ok(TheWorldIndices { y }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let y = _instance + .get_export(&mut store, None, "y") + .ok_or_else(|| anyhow::anyhow!("no function export `y` found"))?; + Ok(TheWorldIndices { y }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let y = *_instance.get_typed_func::<(), ()>(&mut store, &self.y)?.func(); + Ok(TheWorld { y }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub async fn call_y( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) + }; + let promise = callee.call_concurrent(store.as_context_mut(), ()).await?; + Ok(promise) + } + } +}; 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 498170dd624f..dc0eeb1703d7 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 1fe656ec53d9..c9616d3c9c04 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_concurrent.rs b/crates/component-macro/tests/expanded/smoke-export_concurrent.rs new file mode 100644 index 000000000000..50a067394f96 --- /dev/null +++ b/crates/component-macro/tests/expanded/smoke-export_concurrent.rs @@ -0,0 +1,268 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::the_name::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::the_name::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::the_name::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::the_name::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn the_name(&self) -> &exports::the_name::Guest { + &self.interface0 + } + } +}; +pub mod exports { + #[allow(clippy::all)] + pub mod the_name { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + y: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + y: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "the-name") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `the-name`") + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "the-name") + .ok_or_else(|| { + anyhow::anyhow!("no exported instance named `the-name`") + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `the-name` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let y = lookup("y")?; + Ok(GuestIndices { y }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let y = *_instance.get_typed_func::<(), ()>(&mut store, &self.y)?.func(); + Ok(Guest { y }) + } + } + impl Guest { + pub async fn call_y( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::<(), ()>::new_unchecked(self.y) + }; + let promise = callee.call_concurrent(store.as_context_mut(), ()).await?; + Ok(promise) + } + } + } +} 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 c4ef584cdc22..74a4831563c8 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 97e1b0fe51b6..ac9cc05dab6c 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 441d2f7eb40a..dce2350e77d6 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_concurrent.rs b/crates/component-macro/tests/expanded/smoke_concurrent.rs new file mode 100644 index 000000000000..cc988d124173 --- /dev/null +++ b/crates/component-macro/tests/expanded/smoke_concurrent.rs @@ -0,0 +1,271 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(TheWorldIndices {}) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(TheWorldIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(TheWorld {}) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + imports::Host + 'static, + U: Send + imports::Host, + { + imports::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +#[allow(clippy::all)] +pub mod imports { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn y( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("imports")?; + inst.func_wrap_concurrent( + "y", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::y(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn y( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::y(store) + } + } +} diff --git a/crates/component-macro/tests/expanded/smoke_tracing_async.rs b/crates/component-macro/tests/expanded/smoke_tracing_async.rs index 02b979f4a7bb..fd20d70c4a14 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 40b22082c941..98e2e1159cff 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 4d448ab0a345..89953a480a6a 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_concurrent.rs b/crates/component-macro/tests/expanded/strings_concurrent.rs new file mode 100644 index 000000000000..9db4c9b6a570 --- /dev/null +++ b/crates/component-macro/tests/expanded/strings_concurrent.rs @@ -0,0 +1,592 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices { + interface0: exports::foo::foo::strings::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld { + interface0: exports::foo::foo::strings::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::strings::GuestIndices::new(_component)?; + Ok(TheWorldIndices { interface0 }) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::strings::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(TheWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(TheWorld { interface0 }) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::strings::Host + 'static, + U: Send + foo::foo::strings::Host, + { + foo::foo::strings::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_strings(&self) -> &exports::foo::foo::strings::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod strings { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub trait Host { + type Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::String, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn b( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::String + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn c( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::String, + b: wasmtime::component::__internal::String, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::String + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/strings")?; + inst.func_wrap_concurrent( + "a", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::__internal::String,)| + { + let host = caller; + let r = ::a(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "b", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::b(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::String,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::String,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "c", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + ): ( + wasmtime::component::__internal::String, + wasmtime::component::__internal::String, + )| + { + let host = caller; + let r = ::c(host, arg0, arg1); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::String,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (wasmtime::component::__internal::String,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: wasmtime::component::__internal::String, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a(store, x) + } + fn b( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::String + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::b(store) + } + fn c( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: wasmtime::component::__internal::String, + b: wasmtime::component::__internal::String, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> wasmtime::component::__internal::String + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::c(store, a, b) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod strings { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub struct Guest { + a: wasmtime::component::Func, + b: wasmtime::component::Func, + c: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + a: wasmtime::component::ComponentExportIndex, + b: wasmtime::component::ComponentExportIndex, + c: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/strings") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/strings`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/strings") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/strings`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/strings` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let a = lookup("a")?; + let b = lookup("b")?; + let c = lookup("c")?; + Ok(GuestIndices { a, b, c }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let a = *_instance + .get_typed_func::<(&str,), ()>(&mut store, &self.a)? + .func(); + let b = *_instance + .get_typed_func::< + (), + (wasmtime::component::__internal::String,), + >(&mut store, &self.b)? + .func(); + let c = *_instance + .get_typed_func::< + (&str, &str), + (wasmtime::component::__internal::String,), + >(&mut store, &self.c)? + .func(); + Ok(Guest { a, b, c }) + } + } + impl Guest { + pub async fn call_a( + &self, + mut store: S, + arg0: wasmtime::component::__internal::String, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (wasmtime::component::__internal::String,), + (), + >::new_unchecked(self.a) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_b( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::String, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (wasmtime::component::__internal::String,), + >::new_unchecked(self.b) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_c( + &self, + mut store: S, + arg0: wasmtime::component::__internal::String, + arg1: wasmtime::component::__internal::String, + ) -> wasmtime::Result< + wasmtime::component::Promise< + wasmtime::component::__internal::String, + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + wasmtime::component::__internal::String, + wasmtime::component::__internal::String, + ), + (wasmtime::component::__internal::String,), + >::new_unchecked(self.c) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0, arg1)) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/strings_tracing_async.rs b/crates/component-macro/tests/expanded/strings_tracing_async.rs index 6e19b15240a3..1a4926b65596 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 9ac28a24a454..10e4c261bfb9 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. /// @@ -190,10 +190,11 @@ pub trait TheWorldImports: HostBaz { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -255,7 +256,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) } @@ -268,10 +272,13 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> { let mut linker = linker.root(); if options.experimental_world { @@ -384,7 +391,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 +409,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 23686a38346e..5e3334566644 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) @@ -201,10 +198,11 @@ pub trait TheWorldImports: Send + HostBaz { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -268,7 +266,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 @@ -282,10 +280,13 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -410,7 +411,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 +433,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_concurrent.rs b/crates/component-macro/tests/expanded/unstable-features_concurrent.rs new file mode 100644 index 000000000000..b54043c3e9fe --- /dev/null +++ b/crates/component-macro/tests/expanded/unstable-features_concurrent.rs @@ -0,0 +1,687 @@ +/// Link-time configurations. +#[derive(Clone, Debug, Default)] +pub struct LinkOptions { + experimental_interface: bool, + experimental_interface_function: bool, + experimental_interface_resource: bool, + experimental_interface_resource_method: bool, + experimental_world: bool, + experimental_world_function_import: bool, + experimental_world_interface_import: bool, + experimental_world_resource: bool, + experimental_world_resource_method: bool, +} +impl LinkOptions { + /// Enable members marked as `@unstable(feature = experimental-interface)` + pub fn experimental_interface(&mut self, enabled: bool) -> &mut Self { + self.experimental_interface = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-function)` + pub fn experimental_interface_function(&mut self, enabled: bool) -> &mut Self { + self.experimental_interface_function = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-resource)` + pub fn experimental_interface_resource(&mut self, enabled: bool) -> &mut Self { + self.experimental_interface_resource = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-resource-method)` + pub fn experimental_interface_resource_method( + &mut self, + enabled: bool, + ) -> &mut Self { + self.experimental_interface_resource_method = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-world)` + pub fn experimental_world(&mut self, enabled: bool) -> &mut Self { + self.experimental_world = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-world-function-import)` + pub fn experimental_world_function_import(&mut self, enabled: bool) -> &mut Self { + self.experimental_world_function_import = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-world-interface-import)` + pub fn experimental_world_interface_import(&mut self, enabled: bool) -> &mut Self { + self.experimental_world_interface_import = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-world-resource)` + pub fn experimental_world_resource(&mut self, enabled: bool) -> &mut Self { + self.experimental_world_resource = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-world-resource-method)` + pub fn experimental_world_resource_method(&mut self, enabled: bool) -> &mut Self { + self.experimental_world_resource_method = enabled; + self + } +} +pub enum Baz {} +pub trait HostBaz: Sized { + type BazData; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::BazData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BazData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn drop(&mut self, rep: wasmtime::component::Resource) -> wasmtime::Result<()>; +} +impl<_T: HostBaz> HostBaz for &mut _T { + type BazData = _T::BazData; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::BazData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BazData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostBaz>::foo(store, self_) + } + fn drop(&mut self, rep: wasmtime::component::Resource) -> wasmtime::Result<()> { + HostBaz::drop(*self, rep) + } +} +impl std::convert::From for foo::foo::the_interface::LinkOptions { + fn from(src: LinkOptions) -> Self { + (&src).into() + } +} +impl std::convert::From<&LinkOptions> for foo::foo::the_interface::LinkOptions { + fn from(src: &LinkOptions) -> Self { + let mut dest = Self::default(); + dest.experimental_interface(src.experimental_interface); + dest.experimental_interface_function(src.experimental_interface_function); + dest.experimental_interface_resource(src.experimental_interface_resource); + dest.experimental_interface_resource_method( + src.experimental_interface_resource_method, + ); + dest + } +} +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `the-world`. +/// +/// This structure is created through [`TheWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`TheWorld`] as well. +pub struct TheWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: TheWorldIndices, +} +impl Clone for TheWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> TheWorldPre<_T> { + /// Creates a new copy of `TheWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`TheWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `the-world`. +/// +/// This is an implementation detail of [`TheWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`TheWorld`] as well. +#[derive(Clone)] +pub struct TheWorldIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `the-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`TheWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`TheWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`TheWorldPre::instantiate_async`] to +/// create a [`TheWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`TheWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`TheWorldIndices::new_instance`] followed +/// by [`TheWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct TheWorld {} +pub trait TheWorldImports: HostBaz { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; +} +pub trait TheWorldImportsGetHost< + T, + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: TheWorldImports; +} +impl TheWorldImportsGetHost for F +where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: TheWorldImports, +{ + type Host = O; +} +impl<_T: TheWorldImports> TheWorldImports for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as TheWorldImports>::foo(store) + } +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl TheWorldIndices { + /// Creates a new copy of `TheWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(TheWorldIndices {}) + } + /// Creates a new instance of [`TheWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`TheWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`TheWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(TheWorldIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`TheWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(TheWorld {}) + } + } + impl TheWorld { + /// Convenience wrapper around [`TheWorldPre::new`] and + /// [`TheWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + TheWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`TheWorldIndices::new_instance`] and + /// [`TheWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = TheWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost< + &'a mut T, + T, + Host: TheWorldImports, + >, + >( + linker: &mut wasmtime::component::Linker, + options: &LinkOptions, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut linker = linker.root(); + if options.experimental_world { + if options.experimental_world_resource { + linker + .resource( + "baz", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostBaz::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + } + if options.experimental_world_function_import { + linker + .func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move | + mut caller: wasmtime::StoreContextMut<'_, T>| + { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + } + if options.experimental_world_resource_method { + linker + .func_wrap_concurrent( + "[method]baz.foo", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::foo(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move | + mut caller: wasmtime::StoreContextMut<'_, T>| + { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + } + } + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + options: &LinkOptions, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::the_interface::Host + + TheWorldImports + 'static, + U: Send + foo::foo::the_interface::Host + + TheWorldImports, + { + if options.experimental_world { + Self::add_to_linker_imports_get_host(linker, options, get)?; + if options.experimental_world_interface_import { + foo::foo::the_interface::add_to_linker( + linker, + &options.into(), + get, + )?; + } + } + Ok(()) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod the_interface { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + /// Link-time configurations. + #[derive(Clone, Debug, Default)] + pub struct LinkOptions { + experimental_interface: bool, + experimental_interface_function: bool, + experimental_interface_resource: bool, + experimental_interface_resource_method: bool, + } + impl LinkOptions { + /// Enable members marked as `@unstable(feature = experimental-interface)` + pub fn experimental_interface(&mut self, enabled: bool) -> &mut Self { + self.experimental_interface = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-function)` + pub fn experimental_interface_function( + &mut self, + enabled: bool, + ) -> &mut Self { + self.experimental_interface_function = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-resource)` + pub fn experimental_interface_resource( + &mut self, + enabled: bool, + ) -> &mut Self { + self.experimental_interface_resource = enabled; + self + } + /// Enable members marked as `@unstable(feature = experimental-interface-resource-method)` + pub fn experimental_interface_resource_method( + &mut self, + enabled: bool, + ) -> &mut Self { + self.experimental_interface_resource_method = enabled; + self + } + } + pub enum Bar {} + pub trait HostBar: Sized { + type BarData; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()>; + } + impl<_T: HostBar> HostBar for &mut _T { + type BarData = _T::BarData; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::BarData>, + self_: wasmtime::component::Resource, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::BarData>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as HostBar>::foo(store, self_) + } + fn drop( + &mut self, + rep: wasmtime::component::Resource, + ) -> wasmtime::Result<()> { + HostBar::drop(*self, rep) + } + } + pub trait Host: HostBar + Sized { + type Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost< + &'a mut T, + T, + Host: Host + Send, + >, + >( + linker: &mut wasmtime::component::Linker, + options: &LinkOptions, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + if options.experimental_interface { + let mut inst = linker.instance("foo:foo/the-interface")?; + if options.experimental_interface_resource { + inst.resource( + "bar", + wasmtime::component::ResourceType::host::(), + move |mut store, rep| -> wasmtime::Result<()> { + HostBar::drop( + &mut host_getter(store.data_mut()), + wasmtime::component::Resource::new_own(rep), + ) + }, + )?; + } + if options.experimental_interface_function { + inst.func_wrap_concurrent( + "foo", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::foo(host); + Box::pin(async move { + let fun = r.await; + Box::new(move | + mut caller: wasmtime::StoreContextMut<'_, T>| + { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + } + if options.experimental_interface_resource_method { + inst.func_wrap_concurrent( + "[method]bar.foo", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (wasmtime::component::Resource,)| + { + let host = caller; + let r = ::foo(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move | + mut caller: wasmtime::StoreContextMut<'_, T>| + { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + } + } + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + options: &LinkOptions, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, options, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn foo( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::foo(store) + } + } + } + } +} 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 500bc93d285b..6feb4694e230 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) @@ -201,10 +198,11 @@ pub trait TheWorldImports: Send + HostBaz { } pub trait TheWorldImportsGetHost< T, ->: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + D, +>: Fn(T) -> >::Host + Send + Sync + Copy + 'static { type Host: TheWorldImports; } -impl TheWorldImportsGetHost for F +impl TheWorldImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, O: TheWorldImports, @@ -268,7 +266,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 @@ -282,10 +280,13 @@ const _: () = { let indices = TheWorldIndices::new_instance(&mut store, instance)?; indices.load(store, instance) } - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> TheWorldImportsGetHost<&'a mut T, T, Host: TheWorldImports>, + >( linker: &mut wasmtime::component::Linker, options: &LinkOptions, - host_getter: impl for<'a> TheWorldImportsGetHost<&'a mut T>, + host_getter: G, ) -> wasmtime::Result<()> where T: Send, @@ -439,7 +440,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 +462,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 bec71942c97c..525ebf03f468 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 c7c5c8d9d53a..be18ba76c17d 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_concurrent.rs b/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs new file mode 100644 index 000000000000..208d691bc4fd --- /dev/null +++ b/crates/component-macro/tests/expanded/unversioned-foo_concurrent.rs @@ -0,0 +1,303 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `nope`. +/// +/// This structure is created through [`NopePre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Nope`] as well. +pub struct NopePre { + instance_pre: wasmtime::component::InstancePre, + indices: NopeIndices, +} +impl Clone for NopePre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> NopePre<_T> { + /// Creates a new copy of `NopePre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = NopeIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Nope`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `nope`. +/// +/// This is an implementation detail of [`NopePre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Nope`] as well. +#[derive(Clone)] +pub struct NopeIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `nope`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Nope::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`NopePre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`NopePre::instantiate_async`] to +/// create a [`Nope`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Nope::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`NopeIndices::new_instance`] followed +/// by [`NopeIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Nope {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl NopeIndices { + /// Creates a new copy of `NopeIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(NopeIndices {}) + } + /// Creates a new instance of [`NopeIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Nope`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Nope`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(NopeIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Nope`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(Nope {}) + } + } + impl Nope { + /// Convenience wrapper around [`NopePre::new`] and + /// [`NopePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + NopePre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`NopeIndices::new_instance`] and + /// [`NopeIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = NopeIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::a::Host + 'static, + U: Send + foo::foo::a::Host, + { + foo::foo::a::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum Error { + #[component(name = "other")] + Other(wasmtime::component::__internal::String), + } + impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Error::Other(e) => { + f.debug_tuple("Error::Other").field(e).finish() + } + } + } + } + impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self) + } + } + impl std::error::Error for Error {} + const _: () = { + assert!(12 == < Error as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Error as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn g( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), Error> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/a")?; + inst.func_wrap_concurrent( + "g", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::g(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), Error>,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), Error>,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn g( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), Error> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::g(store) + } + } + } + } +} 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 489b1855dd81..3763977ebfe4 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 10b39e821120..88119b778a8b 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 43033c158764..cdda7f458452 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_concurrent.rs b/crates/component-macro/tests/expanded/use-paths_concurrent.rs new file mode 100644 index 000000000000..07ee4f97b1f0 --- /dev/null +++ b/crates/component-macro/tests/expanded/use-paths_concurrent.rs @@ -0,0 +1,604 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `d`. +/// +/// This structure is created through [`DPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`D`] as well. +pub struct DPre { + instance_pre: wasmtime::component::InstancePre, + indices: DIndices, +} +impl Clone for DPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> DPre<_T> { + /// Creates a new copy of `DPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = DIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`D`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `d`. +/// +/// This is an implementation detail of [`DPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`D`] as well. +#[derive(Clone)] +pub struct DIndices {} +/// Auto-generated bindings for an instance a component which +/// implements the world `d`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`D::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`DPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`DPre::instantiate_async`] to +/// create a [`D`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`D::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`DIndices::new_instance`] followed +/// by [`DIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct D {} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl DIndices { + /// Creates a new copy of `DIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + Ok(DIndices {}) + } + /// Creates a new instance of [`DIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`D`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`D`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(DIndices {}) + } + /// Uses the indices stored in `self` to load an instance + /// of [`D`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + Ok(D {}) + } + } + impl D { + /// Convenience wrapper around [`DPre::new`] and + /// [`DPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + DPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`DIndices::new_instance`] and + /// [`DIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = DIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::a::Host + foo::foo::b::Host + + foo::foo::c::Host + d::Host + 'static, + U: Send + foo::foo::a::Host + foo::foo::b::Host + + foo::foo::c::Host + d::Host, + { + foo::foo::a::add_to_linker(linker, get)?; + foo::foo::b::add_to_linker(linker, get)?; + foo::foo::c::add_to_linker(linker, get)?; + d::add_to_linker(linker, get)?; + Ok(()) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod a { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Foo {} + impl core::fmt::Debug for Foo { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Foo").finish() + } + } + const _: () = { + assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/a")?; + inst.func_wrap_concurrent( + "a", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::a(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a(store) + } + } + } + #[allow(clippy::all)] + pub mod b { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Foo = super::super::super::foo::foo::a::Foo; + const _: () = { + assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/b")?; + inst.func_wrap_concurrent( + "a", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::a(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a(store) + } + } + } + #[allow(clippy::all)] + pub mod c { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Foo = super::super::super::foo::foo::b::Foo; + const _: () = { + assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/c")?; + inst.func_wrap_concurrent( + "a", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::a(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn a( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::a(store) + } + } + } + } +} +#[allow(clippy::all)] +pub mod d { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type Foo = super::foo::foo::c::Foo; + const _: () = { + assert!(0 == < Foo as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Foo as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn b( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("d")?; + inst.func_wrap_concurrent( + "b", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::b(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Foo,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn b( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Foo + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::b(store) + } + } +} 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 6f5f5a69608c..3a4c5839c6c3 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 86e8d659eae5..b9ae7934aee1 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 bf9403156018..f72d376cb1c9 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_concurrent.rs b/crates/component-macro/tests/expanded/variants_concurrent.rs new file mode 100644 index 000000000000..ac9b62aa341d --- /dev/null +++ b/crates/component-macro/tests/expanded/variants_concurrent.rs @@ -0,0 +1,3067 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `my-world`. +/// +/// This structure is created through [`MyWorldPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`MyWorld`] as well. +pub struct MyWorldPre { + instance_pre: wasmtime::component::InstancePre, + indices: MyWorldIndices, +} +impl Clone for MyWorldPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> MyWorldPre<_T> { + /// Creates a new copy of `MyWorldPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = MyWorldIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`MyWorld`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `my-world`. +/// +/// This is an implementation detail of [`MyWorldPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`MyWorld`] as well. +#[derive(Clone)] +pub struct MyWorldIndices { + interface0: exports::foo::foo::variants::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `my-world`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`MyWorld::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`MyWorldPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`MyWorldPre::instantiate_async`] to +/// create a [`MyWorld`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`MyWorld::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`MyWorldIndices::new_instance`] followed +/// by [`MyWorldIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct MyWorld { + interface0: exports::foo::foo::variants::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl MyWorldIndices { + /// Creates a new copy of `MyWorldIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::foo::foo::variants::GuestIndices::new(_component)?; + Ok(MyWorldIndices { interface0 }) + } + /// Creates a new instance of [`MyWorldIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`MyWorld`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`MyWorld`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::foo::foo::variants::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(MyWorldIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`MyWorld`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(MyWorld { interface0 }) + } + } + impl MyWorld { + /// Convenience wrapper around [`MyWorldPre::new`] and + /// [`MyWorldPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + MyWorldPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`MyWorldIndices::new_instance`] and + /// [`MyWorldIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = MyWorldIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::variants::Host + 'static, + U: Send + foo::foo::variants::Host, + { + foo::foo::variants::add_to_linker(linker, get)?; + Ok(()) + } + pub fn foo_foo_variants(&self) -> &exports::foo::foo::variants::Guest { + &self.interface0 + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod variants { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum E1 { + #[component(name = "a")] + A, + } + impl core::fmt::Debug for E1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + E1::A => f.debug_tuple("E1::A").finish(), + } + } + } + const _: () = { + assert!(1 == < E1 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < E1 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Empty {} + impl core::fmt::Debug for Empty { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Empty").finish() + } + } + const _: () = { + assert!(0 == < Empty as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < Empty as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum V1 { + #[component(name = "a")] + A, + #[component(name = "c")] + C(E1), + #[component(name = "d")] + D(wasmtime::component::__internal::String), + #[component(name = "e")] + E(Empty), + #[component(name = "f")] + F, + #[component(name = "g")] + G(u32), + } + impl core::fmt::Debug for V1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + V1::A => f.debug_tuple("V1::A").finish(), + V1::C(e) => f.debug_tuple("V1::C").field(e).finish(), + V1::D(e) => f.debug_tuple("V1::D").field(e).finish(), + V1::E(e) => f.debug_tuple("V1::E").field(e).finish(), + V1::F => f.debug_tuple("V1::F").finish(), + V1::G(e) => f.debug_tuple("V1::G").field(e).finish(), + } + } + } + const _: () = { + assert!(12 == < V1 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < V1 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts1 { + #[component(name = "a")] + A(i32), + #[component(name = "b")] + B(f32), + } + impl core::fmt::Debug for Casts1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts1::A(e) => f.debug_tuple("Casts1::A").field(e).finish(), + Casts1::B(e) => f.debug_tuple("Casts1::B").field(e).finish(), + } + } + } + const _: () = { + assert!(8 == < Casts1 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Casts1 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts2 { + #[component(name = "a")] + A(f64), + #[component(name = "b")] + B(f32), + } + impl core::fmt::Debug for Casts2 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts2::A(e) => f.debug_tuple("Casts2::A").field(e).finish(), + Casts2::B(e) => f.debug_tuple("Casts2::B").field(e).finish(), + } + } + } + const _: () = { + assert!(16 == < Casts2 as wasmtime::component::ComponentType >::SIZE32); + assert!(8 == < Casts2 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts3 { + #[component(name = "a")] + A(f64), + #[component(name = "b")] + B(u64), + } + impl core::fmt::Debug for Casts3 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts3::A(e) => f.debug_tuple("Casts3::A").field(e).finish(), + Casts3::B(e) => f.debug_tuple("Casts3::B").field(e).finish(), + } + } + } + const _: () = { + assert!(16 == < Casts3 as wasmtime::component::ComponentType >::SIZE32); + assert!(8 == < Casts3 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts4 { + #[component(name = "a")] + A(u32), + #[component(name = "b")] + B(i64), + } + impl core::fmt::Debug for Casts4 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts4::A(e) => f.debug_tuple("Casts4::A").field(e).finish(), + Casts4::B(e) => f.debug_tuple("Casts4::B").field(e).finish(), + } + } + } + const _: () = { + assert!(16 == < Casts4 as wasmtime::component::ComponentType >::SIZE32); + assert!(8 == < Casts4 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts5 { + #[component(name = "a")] + A(f32), + #[component(name = "b")] + B(i64), + } + impl core::fmt::Debug for Casts5 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts5::A(e) => f.debug_tuple("Casts5::A").field(e).finish(), + Casts5::B(e) => f.debug_tuple("Casts5::B").field(e).finish(), + } + } + } + const _: () = { + assert!(16 == < Casts5 as wasmtime::component::ComponentType >::SIZE32); + assert!(8 == < Casts5 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts6 { + #[component(name = "a")] + A((f32, u32)), + #[component(name = "b")] + B((u32, u32)), + } + impl core::fmt::Debug for Casts6 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Casts6::A(e) => f.debug_tuple("Casts6::A").field(e).finish(), + Casts6::B(e) => f.debug_tuple("Casts6::B").field(e).finish(), + } + } + } + const _: () = { + assert!(12 == < Casts6 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < Casts6 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum MyErrno { + #[component(name = "bad1")] + Bad1, + #[component(name = "bad2")] + Bad2, + } + impl MyErrno { + pub fn name(&self) -> &'static str { + match self { + MyErrno::Bad1 => "bad1", + MyErrno::Bad2 => "bad2", + } + } + pub fn message(&self) -> &'static str { + match self { + MyErrno::Bad1 => "", + MyErrno::Bad2 => "", + } + } + } + impl core::fmt::Debug for MyErrno { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("MyErrno") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl core::fmt::Display for MyErrno { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + impl std::error::Error for MyErrno {} + const _: () = { + assert!(1 == < MyErrno as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < MyErrno as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct IsClone { + #[component(name = "v1")] + pub v1: V1, + } + impl core::fmt::Debug for IsClone { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("IsClone").field("v1", &self.v1).finish() + } + } + const _: () = { + assert!(12 == < IsClone as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host { + type Data; + fn e1_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: E1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn e1_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> E1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn v1_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: V1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn v1_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> V1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn bool_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: bool, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn bool_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> bool + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn option_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Option, + b: Option<()>, + c: Option, + d: Option, + e: Option, + g: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn option_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn casts( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Casts1, + b: Casts2, + c: Casts3, + d: Casts4, + e: Casts5, + f: Casts6, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Casts1, + Casts2, + Casts3, + Casts4, + Casts5, + Casts6, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Result<(), ()>, + b: Result<(), E1>, + c: Result, + d: Result<(), ()>, + e: Result, + f: Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_result_sugar( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_result_sugar2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), MyErrno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_result_sugar3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_result_sugar4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(i32, u32), MyErrno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_option_sugar( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_option_sugar2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn result_simple( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn is_clone_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: IsClone, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn is_clone_return( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> IsClone + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_named_option( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + fn return_named_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized; + } + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/variants")?; + inst.func_wrap_concurrent( + "e1-arg", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (E1,)| { + let host = caller; + let r = ::e1_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "e1-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::e1_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(E1,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(E1,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "v1-arg", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (arg0,): (V1,)| { + let host = caller; + let r = ::v1_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "v1-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::v1_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(V1,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(V1,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bool-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (bool,)| + { + let host = caller; + let r = ::bool_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "bool-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::bool_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(bool,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(bool,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "option-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ): ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + )| + { + let host = caller; + let r = ::option_arg( + host, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "option-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::option_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "casts", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ): (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6)| + { + let host = caller; + let r = ::casts( + host, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ((Casts1, Casts2, Casts3, Casts4, Casts5, Casts6),), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ((Casts1, Casts2, Casts3, Casts4, Casts5, Casts6),), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + ( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ): ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + )| + { + let host = caller; + let r = ::result_arg( + host, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + ); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::result_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + ), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + ( + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + ), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-result-sugar", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_result_sugar(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-result-sugar2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_result_sugar2(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), MyErrno>,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result<(), MyErrno>,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-result-sugar3", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_result_sugar3(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-result-sugar4", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_result_sugar4(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result<(i32, u32), MyErrno>,), + > + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result< + (Result<(i32, u32), MyErrno>,), + > + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-option-sugar", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_option_sugar(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-option-sugar2", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_option_sugar2(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "result-simple", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::result_simple(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "is-clone-arg", + move | + mut caller: wasmtime::StoreContextMut<'_, T>, + (arg0,): (IsClone,)| + { + let host = caller; + let r = ::is_clone_arg(host, arg0); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok(r) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<()> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "is-clone-return", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::is_clone_return(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(IsClone,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(IsClone,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-named-option", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_named_option(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Option,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + inst.func_wrap_concurrent( + "return-named-result", + move |mut caller: wasmtime::StoreContextMut<'_, T>, (): ()| { + let host = caller; + let r = ::return_named_result(host); + Box::pin(async move { + let fun = r.await; + Box::new(move |mut caller: wasmtime::StoreContextMut<'_, T>| { + let r = fun(caller); + Ok((r,)) + }) + as Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + > + }) + as ::std::pin::Pin< + Box< + dyn ::std::future::Future< + Output = Box< + dyn FnOnce( + wasmtime::StoreContextMut<'_, T>, + ) -> wasmtime::Result<(Result,)> + Send + Sync, + >, + > + Send + Sync + 'static, + >, + > + }, + )?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host> Host for &mut _T { + type Data = _T::Data; + fn e1_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: E1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::e1_arg(store, x) + } + fn e1_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> E1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::e1_result(store) + } + fn v1_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: V1, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::v1_arg(store, x) + } + fn v1_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> V1 + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::v1_result(store) + } + fn bool_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + x: bool, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bool_arg(store, x) + } + fn bool_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> bool + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::bool_result(store) + } + fn option_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Option, + b: Option<()>, + c: Option, + d: Option, + e: Option, + g: Option>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_arg(store, a, b, c, d, e, g) + } + fn option_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::option_result(store) + } + fn casts( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Casts1, + b: Casts2, + c: Casts3, + d: Casts4, + e: Casts5, + f: Casts6, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Casts1, + Casts2, + Casts3, + Casts4, + Casts5, + Casts6, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::casts(store, a, b, c, d, e, f) + } + fn result_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: Result<(), ()>, + b: Result<(), E1>, + c: Result, + d: Result<(), ()>, + e: Result, + f: Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_arg(store, a, b, c, d, e, f) + } + fn result_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ) + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_result(store) + } + fn return_result_sugar( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_result_sugar(store) + } + fn return_result_sugar2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(), MyErrno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_result_sugar2(store) + } + fn return_result_sugar3( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_result_sugar3(store) + } + fn return_result_sugar4( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result<(i32, u32), MyErrno> + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_result_sugar4(store) + } + fn return_option_sugar( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_option_sugar(store) + } + fn return_option_sugar2( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_option_sugar2(store) + } + fn result_simple( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::result_simple(store) + } + fn is_clone_arg( + store: wasmtime::StoreContextMut<'_, Self::Data>, + a: IsClone, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> () + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::is_clone_arg(store, a) + } + fn is_clone_return( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> IsClone + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::is_clone_return(store) + } + fn return_named_option( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Option + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_named_option(store) + } + fn return_named_result( + store: wasmtime::StoreContextMut<'_, Self::Data>, + ) -> impl ::std::future::Future< + Output = impl FnOnce( + wasmtime::StoreContextMut<'_, Self::Data>, + ) -> Result + Send + Sync + 'static, + > + Send + Sync + 'static + where + Self: Sized, + { + <_T as Host>::return_named_result(store) + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod variants { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum E1 { + #[component(name = "a")] + A, + } + impl core::fmt::Debug for E1 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + E1::A => f.debug_tuple("E1::A").finish(), + } + } + } + const _: () = { + assert!(1 == < E1 as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < E1 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone, Copy)] + pub struct Empty {} + impl core::fmt::Debug for Empty { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("Empty").finish() + } + } + const _: () = { + assert!( + 0 == < Empty as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < Empty as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone)] + pub enum V1 { + #[component(name = "a")] + A, + #[component(name = "c")] + C(E1), + #[component(name = "d")] + D(wasmtime::component::__internal::String), + #[component(name = "e")] + E(Empty), + #[component(name = "f")] + F, + #[component(name = "g")] + G(u32), + } + impl core::fmt::Debug for V1 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + V1::A => f.debug_tuple("V1::A").finish(), + V1::C(e) => f.debug_tuple("V1::C").field(e).finish(), + V1::D(e) => f.debug_tuple("V1::D").field(e).finish(), + V1::E(e) => f.debug_tuple("V1::E").field(e).finish(), + V1::F => f.debug_tuple("V1::F").finish(), + V1::G(e) => f.debug_tuple("V1::G").field(e).finish(), + } + } + } + const _: () = { + assert!(12 == < V1 as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < V1 as wasmtime::component::ComponentType >::ALIGN32); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts1 { + #[component(name = "a")] + A(i32), + #[component(name = "b")] + B(f32), + } + impl core::fmt::Debug for Casts1 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts1::A(e) => f.debug_tuple("Casts1::A").field(e).finish(), + Casts1::B(e) => f.debug_tuple("Casts1::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 8 == < Casts1 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Casts1 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts2 { + #[component(name = "a")] + A(f64), + #[component(name = "b")] + B(f32), + } + impl core::fmt::Debug for Casts2 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts2::A(e) => f.debug_tuple("Casts2::A").field(e).finish(), + Casts2::B(e) => f.debug_tuple("Casts2::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 16 == < Casts2 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < Casts2 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts3 { + #[component(name = "a")] + A(f64), + #[component(name = "b")] + B(u64), + } + impl core::fmt::Debug for Casts3 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts3::A(e) => f.debug_tuple("Casts3::A").field(e).finish(), + Casts3::B(e) => f.debug_tuple("Casts3::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 16 == < Casts3 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < Casts3 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts4 { + #[component(name = "a")] + A(u32), + #[component(name = "b")] + B(i64), + } + impl core::fmt::Debug for Casts4 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts4::A(e) => f.debug_tuple("Casts4::A").field(e).finish(), + Casts4::B(e) => f.debug_tuple("Casts4::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 16 == < Casts4 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < Casts4 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts5 { + #[component(name = "a")] + A(f32), + #[component(name = "b")] + B(i64), + } + impl core::fmt::Debug for Casts5 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts5::A(e) => f.debug_tuple("Casts5::A").field(e).finish(), + Casts5::B(e) => f.debug_tuple("Casts5::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 16 == < Casts5 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 8 == < Casts5 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(variant)] + #[derive(Clone, Copy)] + pub enum Casts6 { + #[component(name = "a")] + A((f32, u32)), + #[component(name = "b")] + B((u32, u32)), + } + impl core::fmt::Debug for Casts6 { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match self { + Casts6::A(e) => f.debug_tuple("Casts6::A").field(e).finish(), + Casts6::B(e) => f.debug_tuple("Casts6::B").field(e).finish(), + } + } + } + const _: () = { + assert!( + 12 == < Casts6 as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < Casts6 as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(enum)] + #[derive(Clone, Copy, Eq, PartialEq)] + #[repr(u8)] + pub enum MyErrno { + #[component(name = "bad1")] + Bad1, + #[component(name = "bad2")] + Bad2, + } + impl MyErrno { + pub fn name(&self) -> &'static str { + match self { + MyErrno::Bad1 => "bad1", + MyErrno::Bad2 => "bad2", + } + } + pub fn message(&self) -> &'static str { + match self { + MyErrno::Bad1 => "", + MyErrno::Bad2 => "", + } + } + } + impl core::fmt::Debug for MyErrno { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("MyErrno") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl core::fmt::Display for MyErrno { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + impl std::error::Error for MyErrno {} + const _: () = { + assert!( + 1 == < MyErrno as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 1 == < MyErrno as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + #[derive(Clone)] + pub struct IsClone { + #[component(name = "v1")] + pub v1: V1, + } + impl core::fmt::Debug for IsClone { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("IsClone").field("v1", &self.v1).finish() + } + } + const _: () = { + assert!( + 12 == < IsClone as wasmtime::component::ComponentType >::SIZE32 + ); + assert!( + 4 == < IsClone as wasmtime::component::ComponentType >::ALIGN32 + ); + }; + pub struct Guest { + e1_arg: wasmtime::component::Func, + e1_result: wasmtime::component::Func, + v1_arg: wasmtime::component::Func, + v1_result: wasmtime::component::Func, + bool_arg: wasmtime::component::Func, + bool_result: wasmtime::component::Func, + option_arg: wasmtime::component::Func, + option_result: wasmtime::component::Func, + casts: wasmtime::component::Func, + result_arg: wasmtime::component::Func, + result_result: wasmtime::component::Func, + return_result_sugar: wasmtime::component::Func, + return_result_sugar2: wasmtime::component::Func, + return_result_sugar3: wasmtime::component::Func, + return_result_sugar4: wasmtime::component::Func, + return_option_sugar: wasmtime::component::Func, + return_option_sugar2: wasmtime::component::Func, + result_simple: wasmtime::component::Func, + is_clone_arg: wasmtime::component::Func, + is_clone_return: wasmtime::component::Func, + return_named_option: wasmtime::component::Func, + return_named_result: wasmtime::component::Func, + } + #[derive(Clone)] + pub struct GuestIndices { + e1_arg: wasmtime::component::ComponentExportIndex, + e1_result: wasmtime::component::ComponentExportIndex, + v1_arg: wasmtime::component::ComponentExportIndex, + v1_result: wasmtime::component::ComponentExportIndex, + bool_arg: wasmtime::component::ComponentExportIndex, + bool_result: wasmtime::component::ComponentExportIndex, + option_arg: wasmtime::component::ComponentExportIndex, + option_result: wasmtime::component::ComponentExportIndex, + casts: wasmtime::component::ComponentExportIndex, + result_arg: wasmtime::component::ComponentExportIndex, + result_result: wasmtime::component::ComponentExportIndex, + return_result_sugar: wasmtime::component::ComponentExportIndex, + return_result_sugar2: wasmtime::component::ComponentExportIndex, + return_result_sugar3: wasmtime::component::ComponentExportIndex, + return_result_sugar4: wasmtime::component::ComponentExportIndex, + return_option_sugar: wasmtime::component::ComponentExportIndex, + return_option_sugar2: wasmtime::component::ComponentExportIndex, + result_simple: wasmtime::component::ComponentExportIndex, + is_clone_arg: wasmtime::component::ComponentExportIndex, + is_clone_return: wasmtime::component::ComponentExportIndex, + return_named_option: wasmtime::component::ComponentExportIndex, + return_named_result: wasmtime::component::ComponentExportIndex, + } + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "foo:foo/variants") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/variants`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export(&mut store, None, "foo:foo/variants") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `foo:foo/variants`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `foo:foo/variants` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + let e1_arg = lookup("e1-arg")?; + let e1_result = lookup("e1-result")?; + let v1_arg = lookup("v1-arg")?; + let v1_result = lookup("v1-result")?; + let bool_arg = lookup("bool-arg")?; + let bool_result = lookup("bool-result")?; + let option_arg = lookup("option-arg")?; + let option_result = lookup("option-result")?; + let casts = lookup("casts")?; + let result_arg = lookup("result-arg")?; + let result_result = lookup("result-result")?; + let return_result_sugar = lookup("return-result-sugar")?; + let return_result_sugar2 = lookup("return-result-sugar2")?; + let return_result_sugar3 = lookup("return-result-sugar3")?; + let return_result_sugar4 = lookup("return-result-sugar4")?; + let return_option_sugar = lookup("return-option-sugar")?; + let return_option_sugar2 = lookup("return-option-sugar2")?; + let result_simple = lookup("result-simple")?; + let is_clone_arg = lookup("is-clone-arg")?; + let is_clone_return = lookup("is-clone-return")?; + let return_named_option = lookup("return-named-option")?; + let return_named_result = lookup("return-named-result")?; + Ok(GuestIndices { + e1_arg, + e1_result, + v1_arg, + v1_result, + bool_arg, + bool_result, + option_arg, + option_result, + casts, + result_arg, + result_result, + return_result_sugar, + return_result_sugar2, + return_result_sugar3, + return_result_sugar4, + return_option_sugar, + return_option_sugar2, + result_simple, + is_clone_arg, + is_clone_return, + return_named_option, + return_named_result, + }) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + let e1_arg = *_instance + .get_typed_func::<(E1,), ()>(&mut store, &self.e1_arg)? + .func(); + let e1_result = *_instance + .get_typed_func::<(), (E1,)>(&mut store, &self.e1_result)? + .func(); + let v1_arg = *_instance + .get_typed_func::<(&V1,), ()>(&mut store, &self.v1_arg)? + .func(); + let v1_result = *_instance + .get_typed_func::<(), (V1,)>(&mut store, &self.v1_result)? + .func(); + let bool_arg = *_instance + .get_typed_func::<(bool,), ()>(&mut store, &self.bool_arg)? + .func(); + let bool_result = *_instance + .get_typed_func::< + (), + (bool,), + >(&mut store, &self.bool_result)? + .func(); + let option_arg = *_instance + .get_typed_func::< + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + (), + >(&mut store, &self.option_arg)? + .func(); + let option_result = *_instance + .get_typed_func::< + (), + ( + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + ), + >(&mut store, &self.option_result)? + .func(); + let casts = *_instance + .get_typed_func::< + (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + ((Casts1, Casts2, Casts3, Casts4, Casts5, Casts6),), + >(&mut store, &self.casts)? + .func(); + let result_arg = *_instance + .get_typed_func::< + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result<&str, &[u8]>, + ), + (), + >(&mut store, &self.result_arg)? + .func(); + let result_result = *_instance + .get_typed_func::< + (), + ( + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + ), + >(&mut store, &self.result_result)? + .func(); + let return_result_sugar = *_instance + .get_typed_func::< + (), + (Result,), + >(&mut store, &self.return_result_sugar)? + .func(); + let return_result_sugar2 = *_instance + .get_typed_func::< + (), + (Result<(), MyErrno>,), + >(&mut store, &self.return_result_sugar2)? + .func(); + let return_result_sugar3 = *_instance + .get_typed_func::< + (), + (Result,), + >(&mut store, &self.return_result_sugar3)? + .func(); + let return_result_sugar4 = *_instance + .get_typed_func::< + (), + (Result<(i32, u32), MyErrno>,), + >(&mut store, &self.return_result_sugar4)? + .func(); + let return_option_sugar = *_instance + .get_typed_func::< + (), + (Option,), + >(&mut store, &self.return_option_sugar)? + .func(); + let return_option_sugar2 = *_instance + .get_typed_func::< + (), + (Option,), + >(&mut store, &self.return_option_sugar2)? + .func(); + let result_simple = *_instance + .get_typed_func::< + (), + (Result,), + >(&mut store, &self.result_simple)? + .func(); + let is_clone_arg = *_instance + .get_typed_func::< + (&IsClone,), + (), + >(&mut store, &self.is_clone_arg)? + .func(); + let is_clone_return = *_instance + .get_typed_func::< + (), + (IsClone,), + >(&mut store, &self.is_clone_return)? + .func(); + let return_named_option = *_instance + .get_typed_func::< + (), + (Option,), + >(&mut store, &self.return_named_option)? + .func(); + let return_named_result = *_instance + .get_typed_func::< + (), + (Result,), + >(&mut store, &self.return_named_result)? + .func(); + Ok(Guest { + e1_arg, + e1_result, + v1_arg, + v1_result, + bool_arg, + bool_result, + option_arg, + option_result, + casts, + result_arg, + result_result, + return_result_sugar, + return_result_sugar2, + return_result_sugar3, + return_result_sugar4, + return_option_sugar, + return_option_sugar2, + result_simple, + is_clone_arg, + is_clone_return, + return_named_option, + return_named_result, + }) + } + } + impl Guest { + pub async fn call_e1_arg( + &self, + mut store: S, + arg0: E1, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (E1,), + (), + >::new_unchecked(self.e1_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_e1_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (E1,), + >::new_unchecked(self.e1_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_v1_arg( + &self, + mut store: S, + arg0: V1, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (V1,), + (), + >::new_unchecked(self.v1_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_v1_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (V1,), + >::new_unchecked(self.v1_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_bool_arg( + &self, + mut store: S, + arg0: bool, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (bool,), + (), + >::new_unchecked(self.bool_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_bool_result( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (bool,), + >::new_unchecked(self.bool_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_option_arg( + &self, + mut store: S, + arg0: Option, + arg1: Option<()>, + arg2: Option, + arg3: Option, + arg4: Option, + arg5: Option>, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + (), + >::new_unchecked(self.option_arg) + }; + let promise = callee + .call_concurrent( + store.as_context_mut(), + (arg0, arg1, arg2, arg3, arg4, arg5), + ) + .await?; + Ok(promise) + } + pub async fn call_option_result( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ( + ( + Option, + Option<()>, + Option, + Option, + Option, + Option>, + ), + ), + >::new_unchecked(self.option_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_casts( + &self, + mut store: S, + arg0: Casts1, + arg1: Casts2, + arg2: Casts3, + arg3: Casts4, + arg4: Casts5, + arg5: Casts6, + ) -> wasmtime::Result< + wasmtime::component::Promise< + (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (Casts1, Casts2, Casts3, Casts4, Casts5, Casts6), + ((Casts1, Casts2, Casts3, Casts4, Casts5, Casts6),), + >::new_unchecked(self.casts) + }; + let promise = callee + .call_concurrent( + store.as_context_mut(), + (arg0, arg1, arg2, arg3, arg4, arg5), + ) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_result_arg( + &self, + mut store: S, + arg0: Result<(), ()>, + arg1: Result<(), E1>, + arg2: Result, + arg3: Result<(), ()>, + arg4: Result, + arg5: Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + (), + >::new_unchecked(self.result_arg) + }; + let promise = callee + .call_concurrent( + store.as_context_mut(), + (arg0, arg1, arg2, arg3, arg4, arg5), + ) + .await?; + Ok(promise) + } + pub async fn call_result_result( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise< + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + >, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + ( + ( + Result<(), ()>, + Result<(), E1>, + Result, + Result<(), ()>, + Result, + Result< + wasmtime::component::__internal::String, + wasmtime::component::__internal::Vec, + >, + ), + ), + >::new_unchecked(self.result_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_result_sugar( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result,), + >::new_unchecked(self.return_result_sugar) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_result_sugar2( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result<(), MyErrno>,), + >::new_unchecked(self.return_result_sugar2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_result_sugar3( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result,), + >::new_unchecked(self.return_result_sugar3) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_result_sugar4( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result<(i32, u32), MyErrno>,), + >::new_unchecked(self.return_result_sugar4) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_option_sugar( + &self, + mut store: S, + ) -> wasmtime::Result>> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Option,), + >::new_unchecked(self.return_option_sugar) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_option_sugar2( + &self, + mut store: S, + ) -> wasmtime::Result>> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Option,), + >::new_unchecked(self.return_option_sugar2) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_result_simple( + &self, + mut store: S, + ) -> wasmtime::Result>> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result,), + >::new_unchecked(self.result_simple) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_is_clone_arg( + &self, + mut store: S, + arg0: IsClone, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (IsClone,), + (), + >::new_unchecked(self.is_clone_arg) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), (arg0,)) + .await?; + Ok(promise) + } + pub async fn call_is_clone_return( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (IsClone,), + >::new_unchecked(self.is_clone_return) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_named_option( + &self, + mut store: S, + ) -> wasmtime::Result>> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Option,), + >::new_unchecked(self.return_named_option) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + pub async fn call_return_named_result( + &self, + mut store: S, + ) -> wasmtime::Result< + wasmtime::component::Promise>, + > + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::< + (), + (Result,), + >::new_unchecked(self.return_named_result) + }; + let promise = callee + .call_concurrent(store.as_context_mut(), ()) + .await?; + Ok(promise.map(|(v,)| v)) + } + } + } + } + } +} diff --git a/crates/component-macro/tests/expanded/variants_tracing_async.rs b/crates/component-macro/tests/expanded/variants_tracing_async.rs index 089de949b552..18165f6f8ab7 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 de05578b9da7..afe958e43b9d 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 17864d24d90c..242d53736be9 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_concurrent.rs b/crates/component-macro/tests/expanded/wat_concurrent.rs new file mode 100644 index 000000000000..242d53736be9 --- /dev/null +++ b/crates/component-macro/tests/expanded/wat_concurrent.rs @@ -0,0 +1,268 @@ +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `example`. +/// +/// This structure is created through [`ExamplePre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Example`] as well. +pub struct ExamplePre { + instance_pre: wasmtime::component::InstancePre, + indices: ExampleIndices, +} +impl Clone for ExamplePre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> ExamplePre<_T> { + /// Creates a new copy of `ExamplePre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = ExampleIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Example`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `example`. +/// +/// This is an implementation detail of [`ExamplePre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Example`] as well. +#[derive(Clone)] +pub struct ExampleIndices { + interface0: exports::same::name::this_name_is_duplicated::GuestIndices, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `example`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Example::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`ExamplePre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`ExamplePre::instantiate_async`] to +/// create a [`Example`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Example::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`ExampleIndices::new_instance`] followed +/// by [`ExampleIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Example { + interface0: exports::same::name::this_name_is_duplicated::Guest, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl ExampleIndices { + /// Creates a new copy of `ExampleIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let interface0 = exports::same::name::this_name_is_duplicated::GuestIndices::new( + _component, + )?; + Ok(ExampleIndices { interface0 }) + } + /// Creates a new instance of [`ExampleIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Example`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Example`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = exports::same::name::this_name_is_duplicated::GuestIndices::new_instance( + &mut store, + _instance, + )?; + Ok(ExampleIndices { interface0 }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Example`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let interface0 = self.interface0.load(&mut store, &_instance)?; + Ok(Example { interface0 }) + } + } + impl Example { + /// Convenience wrapper around [`ExamplePre::new`] and + /// [`ExamplePre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + ExamplePre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`ExampleIndices::new_instance`] and + /// [`ExampleIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = ExampleIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn same_name_this_name_is_duplicated( + &self, + ) -> &exports::same::name::this_name_is_duplicated::Guest { + &self.interface0 + } + } +}; +pub mod exports { + pub mod same { + pub mod name { + #[allow(clippy::all)] + pub mod this_name_is_duplicated { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type ThisNameIsDuplicated = wasmtime::component::ResourceAny; + pub struct GuestThisNameIsDuplicated<'a> { + funcs: &'a Guest, + } + pub struct Guest {} + #[derive(Clone)] + pub struct GuestIndices {} + impl GuestIndices { + /// Constructor for [`GuestIndices`] which takes a + /// [`Component`](wasmtime::component::Component) as input and can be executed + /// before instantiation. + /// + /// This constructor can be used to front-load string lookups to find exports + /// within a component. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let (_, instance) = component + .export_index(None, "same:name/this-name-is-duplicated") + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `same:name/this-name-is-duplicated`" + ) + })?; + Self::_new(|name| { + component.export_index(Some(&instance), name).map(|p| p.1) + }) + } + /// This constructor is similar to [`GuestIndices::new`] except that it + /// performs string lookups after instantiation time. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let instance_export = instance + .get_export( + &mut store, + None, + "same:name/this-name-is-duplicated", + ) + .ok_or_else(|| { + anyhow::anyhow!( + "no exported instance named `same:name/this-name-is-duplicated`" + ) + })?; + Self::_new(|name| { + instance.get_export(&mut store, Some(&instance_export), name) + }) + } + fn _new( + mut lookup: impl FnMut( + &str, + ) -> Option, + ) -> wasmtime::Result { + let mut lookup = move |name| { + lookup(name) + .ok_or_else(|| { + anyhow::anyhow!( + "instance export `same:name/this-name-is-duplicated` does \ + not have export `{name}`" + ) + }) + }; + let _ = &mut lookup; + Ok(GuestIndices {}) + } + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let _ = &mut store; + let _instance = instance; + Ok(Guest {}) + } + } + impl Guest {} + } + } + } +} diff --git a/crates/component-macro/tests/expanded/wat_tracing_async.rs b/crates/component-macro/tests/expanded/wat_tracing_async.rs index 17864d24d90c..242d53736be9 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 216976fd4272..2ef6cc6e3334 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 c5c1b0f3f6c0..6886b3e7762a 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_concurrent.rs b/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs new file mode 100644 index 000000000000..3c6081062aff --- /dev/null +++ b/crates/component-macro/tests/expanded/worlds-with-types_concurrent.rs @@ -0,0 +1,277 @@ +pub type U = foo::foo::i::T; +const _: () = { + assert!(2 == < U as wasmtime::component::ComponentType >::SIZE32); + assert!(2 == < U as wasmtime::component::ComponentType >::ALIGN32); +}; +pub type T = u32; +const _: () = { + assert!(4 == < T as wasmtime::component::ComponentType >::SIZE32); + assert!(4 == < T as wasmtime::component::ComponentType >::ALIGN32); +}; +#[derive(wasmtime::component::ComponentType)] +#[derive(wasmtime::component::Lift)] +#[derive(wasmtime::component::Lower)] +#[component(record)] +#[derive(Clone, Copy)] +pub struct R {} +impl core::fmt::Debug for R { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("R").finish() + } +} +const _: () = { + assert!(0 == < R as wasmtime::component::ComponentType >::SIZE32); + assert!(1 == < R as wasmtime::component::ComponentType >::ALIGN32); +}; +/// Auto-generated bindings for a pre-instantiated version of a +/// component which implements the world `foo`. +/// +/// This structure is created through [`FooPre::new`] which +/// takes a [`InstancePre`](wasmtime::component::InstancePre) that +/// has been created through a [`Linker`](wasmtime::component::Linker). +/// +/// For more information see [`Foo`] as well. +pub struct FooPre { + instance_pre: wasmtime::component::InstancePre, + indices: FooIndices, +} +impl Clone for FooPre { + fn clone(&self) -> Self { + Self { + instance_pre: self.instance_pre.clone(), + indices: self.indices.clone(), + } + } +} +impl<_T: Send + 'static> FooPre<_T> { + /// Creates a new copy of `FooPre` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component behind `instance_pre` + /// does not have the required exports. + pub fn new( + instance_pre: wasmtime::component::InstancePre<_T>, + ) -> wasmtime::Result { + let indices = FooIndices::new(instance_pre.component())?; + Ok(Self { instance_pre, indices }) + } + pub fn engine(&self) -> &wasmtime::Engine { + self.instance_pre.engine() + } + pub fn instance_pre(&self) -> &wasmtime::component::InstancePre<_T> { + &self.instance_pre + } + /// Instantiates a new instance of [`Foo`] within the + /// `store` provided. + /// + /// This function will use `self` as the pre-instantiated + /// instance to perform instantiation. Afterwards the preloaded + /// indices in `self` are used to lookup all exports on the + /// resulting instance. + pub async fn instantiate_async( + &self, + mut store: impl wasmtime::AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + let instance = self.instance_pre.instantiate_async(&mut store).await?; + self.indices.load(&mut store, &instance) + } +} +/// Auto-generated bindings for index of the exports of +/// `foo`. +/// +/// This is an implementation detail of [`FooPre`] and can +/// be constructed if needed as well. +/// +/// For more information see [`Foo`] as well. +#[derive(Clone)] +pub struct FooIndices { + f: wasmtime::component::ComponentExportIndex, +} +/// Auto-generated bindings for an instance a component which +/// implements the world `foo`. +/// +/// This structure can be created through a number of means +/// depending on your requirements and what you have on hand: +/// +/// * The most convenient way is to use +/// [`Foo::instantiate_async`] which only needs a +/// [`Store`], [`Component`], and [`Linker`]. +/// +/// * Alternatively you can create a [`FooPre`] ahead of +/// time with a [`Component`] to front-load string lookups +/// of exports once instead of per-instantiation. This +/// method then uses [`FooPre::instantiate_async`] to +/// create a [`Foo`]. +/// +/// * If you've instantiated the instance yourself already +/// then you can use [`Foo::new`]. +/// +/// * You can also access the guts of instantiation through +/// [`FooIndices::new_instance`] followed +/// by [`FooIndices::load`] to crate an instance of this +/// type. +/// +/// These methods are all equivalent to one another and move +/// around the tradeoff of what work is performed when. +/// +/// [`Store`]: wasmtime::Store +/// [`Component`]: wasmtime::component::Component +/// [`Linker`]: wasmtime::component::Linker +pub struct Foo { + f: wasmtime::component::Func, +} +const _: () = { + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + impl FooIndices { + /// Creates a new copy of `FooIndices` bindings which can then + /// be used to instantiate into a particular store. + /// + /// This method may fail if the component does not have the + /// required exports. + pub fn new( + component: &wasmtime::component::Component, + ) -> wasmtime::Result { + let _component = component; + let f = _component + .export_index(None, "f") + .ok_or_else(|| anyhow::anyhow!("no function export `f` found"))? + .1; + Ok(FooIndices { f }) + } + /// Creates a new instance of [`FooIndices`] from an + /// instantiated component. + /// + /// This method of creating a [`Foo`] will perform string + /// lookups for all exports when this method is called. This + /// will only succeed if the provided instance matches the + /// requirements of [`Foo`]. + pub fn new_instance( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let f = _instance + .get_export(&mut store, None, "f") + .ok_or_else(|| anyhow::anyhow!("no function export `f` found"))?; + Ok(FooIndices { f }) + } + /// Uses the indices stored in `self` to load an instance + /// of [`Foo`] from the instance provided. + /// + /// Note that at this time this method will additionally + /// perform type-checks of all exports. + pub fn load( + &self, + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let _instance = instance; + let f = *_instance + .get_typed_func::<(), ((T, U, R),)>(&mut store, &self.f)? + .func(); + Ok(Foo { f }) + } + } + impl Foo { + /// Convenience wrapper around [`FooPre::new`] and + /// [`FooPre::instantiate_async`]. + pub async fn instantiate_async<_T>( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker<_T>, + ) -> wasmtime::Result + where + _T: Send + 'static, + { + let pre = linker.instantiate_pre(component)?; + FooPre::new(pre)?.instantiate_async(store).await + } + /// Convenience wrapper around [`FooIndices::new_instance`] and + /// [`FooIndices::load`]. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> wasmtime::Result { + let indices = FooIndices::new_instance(&mut store, instance)?; + indices.load(store, instance) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + T: Send + foo::foo::i::Host + 'static, + U: Send + foo::foo::i::Host, + { + foo::foo::i::add_to_linker(linker, get)?; + Ok(()) + } + pub async fn call_f( + &self, + mut store: S, + ) -> wasmtime::Result> + where + ::Data: Send + 'static, + { + let callee = unsafe { + wasmtime::component::TypedFunc::<(), ((T, U, R),)>::new_unchecked(self.f) + }; + let promise = callee.call_concurrent(store.as_context_mut(), ()).await?; + Ok(promise.map(|(v,)| v)) + } + } +}; +pub mod foo { + pub mod foo { + #[allow(clippy::all)] + pub mod i { + #[allow(unused_imports)] + use wasmtime::component::__internal::{anyhow, Box}; + pub type T = u16; + const _: () = { + assert!(2 == < T as wasmtime::component::ComponentType >::SIZE32); + assert!(2 == < T as wasmtime::component::ComponentType >::ALIGN32); + }; + pub trait Host {} + pub trait GetHost< + T, + D, + >: Fn(T) -> >::Host + Send + Sync + Copy + 'static { + type Host: Host + Send; + } + impl GetHost for F + where + F: Fn(T) -> O + Send + Sync + Copy + 'static, + O: Host + Send, + { + type Host = O; + } + pub fn add_to_linker_get_host< + T, + G: for<'a> GetHost<&'a mut T, T, Host: Host + Send>, + >( + linker: &mut wasmtime::component::Linker, + host_getter: G, + ) -> wasmtime::Result<()> + where + T: Send + 'static, + { + let mut inst = linker.instance("foo:foo/i")?; + Ok(()) + } + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> wasmtime::Result<()> + where + U: Host + Send, + T: Send + 'static, + { + add_to_linker_get_host(linker, get) + } + impl<_T: Host + ?Sized> Host for &mut _T {} + } + } +} 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 6970a67b8721..e72a282b71d5 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..afd95ba7c6b8 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,467 @@ 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, + post_return, + } => { + let pointer_type = self.isa.pointer_type(); + self.translate_async_enter_or_exit( + self.offsets.async_exit(), + Some((*callback, *post_return)), + 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_and_post_return: Option<( + Option, + 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, post_return)) = callback_and_post_return { + // 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)); + } + + // post_return: *mut VMFuncRef + host_sig.params.push(ir::AbiParam::new(pointer_type)); + if let Some(post_return) = post_return { + callee_args.push(self.builder.ins().load( + pointer_type, + MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.runtime_post_return(post_return)).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 +600,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 +629,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 +682,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 +793,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 +822,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 +1013,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 +1043,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 +1741,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 46ab2280e096..355fa7453344 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,113 @@ 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 { + callback: Option, + post_return: 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 +400,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 +467,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 +479,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 +520,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 +555,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 +664,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 +695,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 +762,104 @@ 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, + post_return, + } => info::Trampoline::AsyncExitCall { + callback: callback.map(|v| self.runtime_callback(v)), + post_return: post_return.map(|v| self.runtime_post_return(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 +881,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..0539c0f6c98e 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,49 @@ 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 { + /// The callee's callback, if any. + callback: Option, + + /// The callee's post-return function, if any. + post_return: 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 +955,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 8568836278d2..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,36 +623,152 @@ 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 - | wasmparser::CanonicalFunction::TaskPoll { .. } - | wasmparser::CanonicalFunction::TaskYield { .. } - | wasmparser::CanonicalFunction::SubtaskDrop - | wasmparser::CanonicalFunction::StreamNew { .. } - | wasmparser::CanonicalFunction::StreamRead { .. } - | wasmparser::CanonicalFunction::StreamWrite { .. } - | wasmparser::CanonicalFunction::StreamCancelRead { .. } - | wasmparser::CanonicalFunction::StreamCancelWrite { .. } - | wasmparser::CanonicalFunction::StreamCloseReadable { .. } - | wasmparser::CanonicalFunction::StreamCloseWritable { .. } - | wasmparser::CanonicalFunction::FutureNew { .. } - | wasmparser::CanonicalFunction::FutureRead { .. } - | wasmparser::CanonicalFunction::FutureWrite { .. } - | wasmparser::CanonicalFunction::FutureCancelRead { .. } - | wasmparser::CanonicalFunction::FutureCancelWrite { .. } - | wasmparser::CanonicalFunction::FutureCloseReadable { .. } - | wasmparser::CanonicalFunction::FutureCloseWritable { .. } - | wasmparser::CanonicalFunction::ErrorContextNew { .. } - | wasmparser::CanonicalFunction::ErrorContextDebugMessage { .. } - | wasmparser::CanonicalFunction::ErrorContextDrop - | wasmparser::CanonicalFunction::TaskReturn { .. } - | wasmparser::CanonicalFunction::TaskWait { .. } => { - 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); @@ -922,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 { @@ -946,8 +1166,10 @@ impl<'a, 'data> Translator<'a, 'data> { let idx = FuncIndex::from_u32(*idx); ret.post_return = Some(idx); } - wasmparser::CanonicalOption::Async | wasmparser::CanonicalOption::Callback(_) => { - todo!() + wasmparser::CanonicalOption::Async => ret.async_ = true, + wasmparser::CanonicalOption::Callback(idx) => { + let idx = FuncIndex::from_u32(*idx); + ret.callback = Some(idx); } } } diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 8989e6fce1a1..7033269e83fe 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,19 @@ 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, + post_return, + } => simple_intrinsic(dfg::Trampoline::AsyncExitCall { + callback: callback.clone().map(|v| dfg.callbacks.push(v)), + post_return: post_return.clone().map(|v| dfg.post_returns.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 +382,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 4b71255a3cbd..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,9 +426,15 @@ impl ComponentTypesBuilder { ComponentDefinedType::Borrow(r) => { InterfaceType::Borrow(self.resource_id(r.resource())) } - ComponentDefinedType::Future(_) - | ComponentDefinedType::Stream(_) - | ComponentDefinedType::ErrorContext => bail!("unsupported async type"), + 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 { @@ -391,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 { @@ -516,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)?; @@ -568,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) @@ -603,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..ffd8b4073e8c 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,89 @@ 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, + post_return: 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: callback + .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()), + post_return: post_return.map(|post_return| { + self.imported_funcs + .get(post_return) + .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 +621,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 +658,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 +675,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 +743,27 @@ 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 { + /// The callee's callback function, if any. + callback: Option, + + /// The callee's post-return function, if any. + post_return: 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 +862,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 3a5c0f4ce78b..bfc20fefcd5d 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,294 @@ 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, 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())); + + // 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, 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(), + )); + + 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, adapter.lift.post_return); + + 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 +784,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 +816,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 +853,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 +880,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 +911,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 +1033,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 +1072,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 +2903,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 +2960,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 +2976,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 +2986,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 +2999,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/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index 437c7c235ba0..60942506e19e 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -135,6 +135,7 @@ impl Config { extended_const, wide_arithmetic, component_model_more_flags, + component_model_async, simd, hogs_memory: _, @@ -147,6 +148,7 @@ impl Config { self.module_config.function_references_enabled = function_references.or(gc).unwrap_or(false); self.module_config.component_model_more_flags = component_model_more_flags.unwrap_or(false); + self.module_config.component_model_async = component_model_async.unwrap_or(false); // Enable/disable proposals that wasm-smith has knobs for which will be // read when creating `wasmtime::Config`. @@ -260,6 +262,7 @@ impl Config { .wasm_wide_arithmetic(self.module_config.config.wide_arithmetic_enabled) .wasm_extended_const(self.module_config.config.extended_const_enabled) .wasm_component_model_more_flags(self.module_config.component_model_more_flags) + .wasm_component_model_async(self.module_config.component_model_async) .native_unwind_info(cfg!(target_os = "windows") || self.wasmtime.native_unwind_info) .cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans) .cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime()) diff --git a/crates/fuzzing/src/generators/module.rs b/crates/fuzzing/src/generators/module.rs index 283edaf1d2b8..9b8715636399 100644 --- a/crates/fuzzing/src/generators/module.rs +++ b/crates/fuzzing/src/generators/module.rs @@ -16,6 +16,7 @@ pub struct ModuleConfig { // config-to-`wasmtime::Config` translation. pub function_references_enabled: bool, pub component_model_more_flags: bool, + pub component_model_async: bool, } impl<'a> Arbitrary<'a> for ModuleConfig { @@ -62,6 +63,7 @@ impl<'a> Arbitrary<'a> for ModuleConfig { Ok(ModuleConfig { component_model_more_flags: false, + component_model_async: false, function_references_enabled: config.gc_enabled, config, }) 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..4788e83bd5e5 --- /dev/null +++ b/crates/misc/component-async-tests/src/lib.rs @@ -0,0 +1,1162 @@ +#![deny(warnings)] + +#[cfg(test)] +mod test { + use { + anyhow::{anyhow, Result}, + futures::future, + std::{ + future::Future, + ops::DerefMut, + sync::{Arc, Mutex, Once}, + 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, + 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, + }); + } + + fn init_logger() { + static ONCE: Once = Once::new(); + ONCE.call_once(pretty_env_logger::init); + } + + 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<()> { + init_logger(); + + 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() + }, + ) + .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<()> { + init_logger(); + + 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 + } + + // No-op function; we only test this by composing it in `async_post_return_caller` + #[allow( + dead_code, + reason = "here only to make the `assert_test_exists` macro happy" + )] + fn async_post_return_callee() {} + + #[tokio::test] + async fn async_post_return_caller() -> Result<()> { + let caller = &fs::read(test_programs_artifacts::ASYNC_POST_RETURN_CALLER_COMPONENT).await?; + let callee = &fs::read(test_programs_artifacts::ASYNC_POST_RETURN_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<()> { + init_logger(); + + 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, + }; + + init_logger(); + + 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..898aca8c17dc --- /dev/null +++ b/crates/misc/component-async-tests/wit/test.wit @@ -0,0 +1,99 @@ +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>; +} + +interface post-return { + foo: func(s: string) -> string; + get-post-return-value: func() -> string; +} + +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; +} + +world post-return-caller { + import post-return; + export run; +} + +world post-return-callee { + export post-return; +} diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index 07d492b298df..a04ed65bb312 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<()> { @@ -166,6 +174,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: extended_const, wide_arithmetic, component_model_more_flags, + component_model_async, nan_canonicalization, simd, @@ -184,6 +193,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: let extended_const = extended_const.unwrap_or(false); let wide_arithmetic = wide_arithmetic.unwrap_or(false); let component_model_more_flags = component_model_more_flags.unwrap_or(false); + let component_model_async = component_model_async.unwrap_or(false); let nan_canonicalization = nan_canonicalization.unwrap_or(false); let relaxed_simd = relaxed_simd.unwrap_or(false); @@ -210,5 +220,6 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: .wasm_extended_const(extended_const) .wasm_wide_arithmetic(wide_arithmetic) .wasm_component_model_more_flags(component_model_more_flags) + .wasm_component_model_async(component_model_async) .cranelift_nan_canonicalization(nan_canonicalization); } diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml index baff3bba8b79..40210dc8a518 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 } 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/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..90394a65e273 --- /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, + wasi::http::types::{Body, ErrorCode, Request, Response}, + wit_future, wit_stream, + }, + 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) = wit_future::new(); + let (mut pipe_tx, pipe_rx) = wit_stream::new(); + + 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..f65de7cbd3e2 --- /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, + wasi::http::{ + handler, + types::{Body, ErrorCode, Headers, Request, Response}, + }, + wit_future, wit_stream, + }, + 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) = wit_future::new(); + let (mut pipe_tx, pipe_rx) = wit_stream::new(); + + 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) = wit_future::new(); + let (mut pipe_tx, pipe_rx) = wit_stream::new(); + + 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_post_return_callee.rs b/crates/test-programs/src/bin/async_post_return_callee.rs new file mode 100644 index 000000000000..43f58d54f03e --- /dev/null +++ b/crates/test-programs/src/bin/async_post_return_callee.rs @@ -0,0 +1,78 @@ +// Here we avoid using wit-bindgen so that we can export our own post-return +// function and keep track of whether it was called. + +use std::{ + alloc::{self, Layout}, + mem::ManuallyDrop, + sync::Mutex, +}; + +static POST_RETURN_VALUE: Mutex> = Mutex::new(None); + +#[unsafe(export_name = "local:local/post-return#foo")] +unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) -> *mut u8 { + let result = alloc::alloc(Layout::from_size_align(8, 4).unwrap()); + *result.cast::<*mut u8>() = ptr; + *result.add(4).cast::() = len; + result +} + +#[unsafe(export_name = "cabi_post_local:local/post-return#foo")] +unsafe extern "C" fn export_post_return_foo(ptr: *mut u8) { + let s_ptr = *ptr.cast::<*mut u8>(); + let s_len = *ptr.add(4).cast::(); + alloc::dealloc(ptr, Layout::from_size_align(8, 4).unwrap()); + + *POST_RETURN_VALUE.lock().unwrap() = + Some(String::from_utf8(Vec::from_raw_parts(s_ptr, s_len, s_len)).unwrap()); +} + +#[unsafe(export_name = "local:local/post-return#get-post-return-value")] +unsafe extern "C" fn export_get_post_return_value() -> *mut u8 { + let s = ManuallyDrop::new(POST_RETURN_VALUE.lock().unwrap().take().unwrap()); + let result = alloc::alloc(Layout::from_size_align(8, 4).unwrap()); + *result.cast::<*mut u8>() = s.as_ptr().cast_mut(); + *result.add(4).cast::() = s.len(); + result +} + +#[unsafe(export_name = "cabi_post_local:local/post-return#get-post-return-value")] +unsafe extern "C" fn export_post_return_get_post_return_value(ptr: *mut u8) { + let s_ptr = *ptr.cast::<*mut u8>(); + let s_len = *ptr.add(4).cast::(); + alloc::dealloc(ptr, Layout::from_size_align(8, 4).unwrap()); + + drop(String::from_utf8(Vec::from_raw_parts(s_ptr, s_len, s_len)).unwrap()); +} + +#[cfg(target_arch = "wasm32")] +#[unsafe(link_section = "component-type:wit-bindgen:0.37.0:local:local:post-return-callee:encoded world")] +#[doc(hidden)] +#[allow( + clippy::octal_escapes, + reason = "this is a machine-generated binary blob" +)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 255] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07w\x01A\x02\x01A\x02\x01\ +B\x04\x01@\x01\x01ss\0s\x04\0\x03foo\x01\0\x01@\0\0s\x04\0\x15get-post-return-va\ +lue\x01\x01\x04\0\x17local:local/post-return\x05\0\x04\0\x1elocal:local/post-ret\ +urn-callee\x04\0\x0b\x18\x01\0\x12post-return-callee\x03\0\0\0G\x09producers\x01\ +\x0cprocessed-by\x02\x0dwit-component\x070.223.0\x10wit-bindgen-rust\x060.37.0"; + +/// # Safety +/// TODO +#[unsafe(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_post_return_caller.rs b/crates/test-programs/src/bin/async_post_return_caller.rs new file mode 100644 index 000000000000..7e58e59e6668 --- /dev/null +++ b/crates/test-programs/src/bin/async_post_return_caller.rs @@ -0,0 +1,35 @@ +mod bindings { + wit_bindgen::generate!({ + path: "../misc/component-async-tests/wit", + world: "post-return-caller", + async: { + imports: [ + "local:local/post-return#foo" + ], + exports: [ + "local:local/run#run" + ] + } + }); + + use super::Component; + export!(Component); +} + +use bindings::{ + exports::local::local::run::Guest, + local::local::post_return::{foo, get_post_return_value}, +}; + +struct Component; + +impl Guest for Component { + async fn run() { + let s = "All mimsy were the borogoves"; + assert_eq!(s, &foo(s).await); + assert_eq!(s, &get_post_return_value()); + } +} + +// 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..d2ed98f33924 --- /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; + +#[unsafe(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 +#[unsafe(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..b1345e53b5a1 --- /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}, + wit_future, wit_stream, + }, + futures::{SinkExt, StreamExt}, + std::future::IntoFuture, + wit_bindgen_rt::async_support::{self, FutureReader, StreamReader}, +}; + +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) = wit_stream::new(); + let (callee_future_tx1, callee_future_rx1) = wit_future::new(); + let (callee_future_tx2, callee_future_rx2) = wit_future::new(); + + 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..2612ba057030 --- /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}, + wit_future, wit_stream, + }, + 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) = wit_stream::new(); + let (mut caller_stream_tx, caller_stream_rx) = wit_stream::new(); + let (mut caller_future_tx1, caller_future_rx1) = wit_future::new(); + let (caller_future_tx2, caller_future_rx2) = wit_future::new(); + + 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/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 2e3be1e60c27..11408b47216d 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 = ["default-hasher"] } 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 03a807484d96..85d0f5de12a3 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1057,6 +1057,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 6281c123aa4f..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, } @@ -253,7 +254,6 @@ impl Metadata<'_> { assert!(!shared_everything_threads); assert!(!legacy_exceptions); assert!(!stack_switching); - assert!(!component_model_async); Metadata { target: engine.compiler().triple().to_string(), @@ -278,6 +278,7 @@ impl Metadata<'_> { custom_page_sizes, component_model_more_flags, component_model_multiple_returns, + component_model_async, gc_types, wide_arithmetic, }, @@ -488,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; @@ -574,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..b12b4c6009f6 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -0,0 +1,2192 @@ +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}, + ops::Range, + 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. + +#[derive(Clone, Copy, Eq, PartialEq)] +#[repr(u32)] +enum Status { + Starting, + Started, + Returned, + Done, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[repr(u32)] +enum Event { + _Starting, + Started, + Returned, + Done, + _Yielded, + StreamRead, + StreamWrite, + FutureRead, + FutureWrite, +} + +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: Event, + 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 { + fiber: StoreFiber<'static>, + async_: bool, + }, + Stackless { + call: Box Result + Send + Sync + 'static>, + instance: RuntimeComponentInstanceIndex, + callback: SendSyncPtr, + }, +} + +impl Deferred { + fn take_stackful(&mut self) -> Option<(StoreFiber<'static>, bool)> { + if let Self::Stackful { .. } = self { + let Self::Stackful { fiber, async_ } = mem::replace(self, Self::None) else { + unreachable!() + }; + Some((fiber, async_)) + } 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<(Event, 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 == Event::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 DummyResult; + +struct Reset(*mut T, T); + +impl Drop for Reset { + fn drop(&mut self) { + unsafe { + *self.0 = self.1; + } + } +} + +#[derive(Clone, Copy)] +struct PollContext { + future_context: *mut Context<'static>, + guard_range_start: *mut u8, + guard_range_end: *mut u8, +} + +impl Default for PollContext { + fn default() -> PollContext { + PollContext { + future_context: ptr::null_mut(), + guard_range_start: ptr::null_mut(), + guard_range_end: ptr::null_mut(), + } + } +} + +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, +} + +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 PollContext, + track_pkey_context_switch: bool, +} + +impl AsyncCx { + pub(crate) fn new(store: &mut StoreContextMut) -> Self { + Self::try_new(store).unwrap() + } + + pub(crate) fn try_new(store: &mut StoreContextMut) -> Option { + let current_poll_cx = store.concurrent_state().async_state.current_poll_cx.get(); + if unsafe { (*current_poll_cx).future_context.is_null() } { + None + } else { + Some(Self { + current_suspend: store.concurrent_state().async_state.current_suspend.get(), + current_stack_limit: store.0.runtime_limits().stack_limit.get(), + current_poll_cx, + 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 = PollContext::default(); + assert!(!poll_cx.future_context.is_null()); + future.as_mut().poll(&mut *poll_cx.future_context) + } + + 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 ConcurrentState { + pub(crate) fn async_guard_range(&self) -> Range<*mut u8> { + let context = unsafe { *self.async_state.current_poll_cx.get() }; + context.guard_range_start..context.guard_range_end + } +} + +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(PollContext::default()), + }, + 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: Event::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: Event::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: Option, + 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(()) + } + })?; + + let guard_range = fiber + .fiber + .as_ref() + .unwrap() + .stack() + .guard_range() + .map(|r| { + ( + NonNull::new(r.start).map(SendSyncPtr::new), + NonNull::new(r.end).map(SendSyncPtr::new), + ) + }) + .unwrap_or((None, None)); + + store = poll_fn(store, guard_range, 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: Event, + 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 as u32), + 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, Event::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 == Event::Done { + if let Some((fiber, async_)) = store + .concurrent_state() + .table + .get_mut(guest_task)? + .deferred + .take_stackful() + { + 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, async_)?; + 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>, + async_: bool, +) -> Result> { + match resume_fiber(&mut fiber, Some(store), Ok(()))? { + Ok((mut store, result)) => { + result?; + if async_ { + let task = store.concurrent_state().table.get(guest_task)?; + if !(matches!(&task.caller, Caller::Guest {..} if task.result.is_some()) + || matches!(&task.caller, Caller::Host(tx) if tx.is_none())) + { + return Err(anyhow!(crate::Trap::NoAsyncResult)); + } + } + if let Some(instance) = fiber.instance { + store = maybe_resume_next_task(store, 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 == Event::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, Event::Done, AnyTask::Guest(guest_task), 0) + } + } + } else { + Ok(store) + } + } + Err(new_store) => { + store = new_store.unwrap(); + store.concurrent_state().table.get_mut(guest_task)?.deferred = + Deferred::Stackful { fiber, async_ }; + 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() { + Event::Started + } else if guest_context != 0 { + Event::Returned + } else if matches!(&task.caller, Caller::Guest {..} if task.result.is_some()) + || matches!(&task.caller, Caller::Host(tx) if tx.is_none()) + { + Event::Done + } else { + return Err(anyhow!(crate::Trap::NoAsyncResult)); + }; + 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 { + Event::Done => AnyTask::Host(TableId::::new(task)), + Event::StreamRead | Event::FutureRead | Event::StreamWrite | Event::FutureWrite => { + 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, async_)) = store + .concurrent_state() + .table + .get_mut(guest_task)? + .deferred + .take_stackful() + { + resumed = true; + let old_task = store.concurrent_state().guest_task.replace(guest_task); + store = resume_stackful(store, guest_task, fiber, async_)?; + 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, async_ } => resume_stackful(store, task, fiber, async_), + 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: Option, +} + +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: Option, + 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.future_context.is_null()); + while let Poll::Ready(Some(ready)) = store + .concurrent_state() + .futures + .poll_next_unpin(&mut *cx.future_context) + { + 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 as u32) + } + 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 as u32).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()); + + log::trace!("task.return for {}", guest_task.rep()); + + 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); + } else { + cx.concurrent_state().table.get_mut(guest_task)?.result = Some(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), + Event::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), + Event::Returned, + AnyTask::Guest(task), + 0, + )?; + } + Ok(Box::new(DummyResult) as Box) + }), + 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>, + instance: *mut ComponentInstance, + guest_task: TableId, + async_: bool, + call: impl FnOnce( + StoreContextMut, + ) -> Result<([MaybeUninit; MAX_FLAT_PARAMS], StoreContextMut)> + + Send + + Sync + + 'static, + callback: Option>, + post_return: 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 async_finished = false; + + 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; + async_finished = guest_context == 0; + 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, Some(callee_instance), move |mut cx| { + let mut flags = unsafe { (*instance).instance_flags(callee_instance) }; + + 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()) }; + + unsafe { + flags.set_needs_post_return(false); + } + + if let Some(func) = post_return { + let arg = match result_count { + 0 => ValRaw::i32(0), + 1 => unsafe { storage[0].assume_init() }, + _ => unreachable!(), + }; + unsafe { + crate::Func::call_unchecked_raw( + &mut cx, + func.as_non_null(), + ptr::slice_from_raw_parts(&arg, 1).cast_mut(), + )?; + } + } + + unsafe { + flags.set_may_enter(true); + } + + if let Caller::Host(tx) = + &mut cx.concurrent_state().table.get_mut(guest_task)?.caller + { + _ = tx.take().unwrap().send(result); + } else { + cx.concurrent_state().table.get_mut(guest_task)?.result = Some(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)) => { + async_finished = async_; + 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, async_ }; + 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, async_ }; + 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)?; + } + } else if async_finished + && !(matches!(&task.caller, Caller::Guest {..} if task.result.is_some()) + || matches!(&task.caller, Caller::Host(tx) if tx.is_none())) + { + return Err(anyhow!(crate::Trap::NoAsyncResult)); + } + + Ok((guest_context, cx)) +} + +pub(crate) extern "C" fn async_exit( + cx: *mut VMOpaqueContext, + callback: *mut VMFuncRef, + post_return: *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, + instance, + guest_task, + (flags & EXIT_FLAG_ASYNC_CALLEE) != 0, + call, + NonNull::new(callback).map(SendSyncPtr::new), + NonNull::new(post_return).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 as u32) << 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 component_instance = func_data.component_instance; + let instance = func_data.instance; + let callee = func_data.export.func_ref; + let callback = func_data.options.callback; + let post_return = func_data.post_return; + + 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[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); + + let instance = store.0[instance.0].as_ref().unwrap().instance_ptr(); + + store = do_start_call( + store, + instance, + guest_task, + is_concurrent, + call, + callback.map(SendSyncPtr::new), + post_return.map(|f| SendSyncPtr::new(f.func_ref)), + component_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, (None, None), move |_, mut store| { + Ok(handle_ready(store.take().unwrap(), ready.take().unwrap())) + }) + .await?; + } else { + let (s, resumed) = poll_fn(store, (None, None), 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, (None, None), 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>, + guard_range: (Option>, Option>), + mut fun: impl FnMut( + &mut Context, + Option>, + ) -> Result>>, +) -> R { + #[derive(Clone, Copy)] + struct PollCx(*mut PollContext); + + 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); + let guard_range_start = guard_range.0.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut()); + let guard_range_end = guard_range.1.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut()); + *poll_cx.0 = PollContext { + future_context: mem::transmute::<&mut Context<'_>, *mut Context<'static>>(cx), + guard_range_start, + guard_range_end, + }; + #[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..8542e36d9878 --- /dev/null +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -0,0 +1,2070 @@ +use { + super::{ + call_host_and_handle_result, table::TableId, Event, 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, ValRaw, + }, + 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, + }, +}; + +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: Event, + 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) + }; + if address % usize::try_from(T::ALIGN32)? != 0 { + bail!("read pointer not aligned"); + } + lower + .as_slice_mut() + .get_mut(address..) + .and_then(|b| b.get_mut(..T::SIZE32 * count)) + .ok_or_else(|| anyhow::anyhow!("read pointer out of bounds of memory"))?; + + 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(), + ); + if address % usize::try_from(T::ALIGN32)? != 0 { + bail!("read pointer not aligned"); + } + lower + .as_slice_mut() + .get_mut(address..) + .and_then(|b| b.get_mut(..T::SIZE32 * count)) + .ok_or_else(|| anyhow::anyhow!("read pointer out of bounds of memory"))?; + + 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(_) => Event::FutureRead, + TableIndex::Stream(_) => Event::StreamRead, + }, + 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| { + if address % usize::try_from(T::ALIGN32)? != 0 { + bail!("write pointer not aligned"); + } + lift.memory() + .get(address..) + .and_then(|b| b.get(..T::SIZE32 * count)) + .ok_or_else(|| { + anyhow::anyhow!("write pointer out of bounds of memory") + })?; + + 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(_) => Event::FutureWrite, + TableIndex::Stream(_) => Event::StreamWrite, + }, + 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(_) => Event::FutureRead, + TableIndex::Stream(_) => Event::StreamRead, + }, + 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(_) => Event::FutureRead, + TableIndex::Stream(_) => Event::StreamRead, + }, + 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 abi = types.canonical_abi(&ty); + // FIXME: needs to read an i64 for memory64 + if write_address % usize::try_from(abi.align32)? != 0 { + bail!("write pointer not aligned"); + } + + let lift = &mut LiftContext::new(cx.0, write_options, types, instance); + + let bytes = lift + .memory() + .get(write_address..) + .and_then(|b| b.get(..usize::try_from(abi.size32).unwrap())) + .ok_or_else(|| anyhow::anyhow!("write pointer out of bounds of memory"))?; + + Val::load(lift, ty, bytes) + }) + .transpose()?; + + if let Some(val) = val { + let mut lower = + LowerContext::new(cx.as_context_mut(), read_options, types, instance); + let ty = types[types[read_ty].ty].payload.unwrap(); + let ptr = func::validate_inbounds_dynamic( + types.canonical_abi(&ty), + lower.as_slice_mut(), + &ValRaw::u32(read_address.try_into().unwrap()), + )?; + val.store(&mut lower, ty, ptr)?; + } + } + (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; + if write_address % usize::try_from(flat_abi.align)? != 0 { + bail!("write pointer not aligned"); + } + if read_address % usize::try_from(flat_abi.align)? != 0 { + bail!("read pointer not aligned"); + } + + { + let src = write_options + .memory(cx.0) + .get(write_address..) + .and_then(|b| b.get(..length_in_bytes)) + .ok_or_else(|| anyhow::anyhow!("write pointer out of bounds of memory"))? + .as_ptr(); + let dst = read_options + .memory_mut(cx.0) + .get_mut(read_address..) + .and_then(|b| b.get_mut(..length_in_bytes)) + .ok_or_else(|| anyhow::anyhow!("read pointer out of bounds of memory"))? + .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(); + if write_address % usize::try_from(abi.align32)? != 0 { + bail!("write pointer not aligned"); + } + let bytes = lift + .memory() + .get(write_address..) + .and_then(|b| b.get(..size * count)) + .ok_or_else(|| anyhow::anyhow!("write pointer out of bounds of memory"))?; + + let values = (0..count) + .map(|index| Val::load(lift, ty, &bytes[(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 ty = types[types[read_ty].ty].payload; + let abi = lower.types.canonical_abi(&ty); + if read_address % usize::try_from(abi.align32)? != 0 { + bail!("read pointer not aligned"); + } + let size = usize::try_from(abi.size32).unwrap(); + lower + .as_slice_mut() + .get_mut(read_address..) + .and_then(|b| b.get_mut(..size * count)) + .ok_or_else(|| anyhow::anyhow!("read pointer out of bounds of memory"))?; + let mut ptr = read_address; + 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(_) => Event::FutureRead, + TableIndex::Stream(_) => Event::StreamRead, + }, + 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(_) => Event::FutureWrite, + TableIndex::Stream(_) => Event::StreamWrite, + }, + 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 1bfcefff8e2b..93999d7ab038 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,17 +46,17 @@ 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, - post_return: Option, + 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, + pub(crate) 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,98 @@ 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, Some(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? + } } - fn call_impl( + /// 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, Some(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 = Self::lower_args as LowerFn<_, _, _>; + let lift = if store.0[self.0].options.async_() { + Self::lift_results_async as LiftFn<_> + } else { + Self::lift_results_sync as LiftFn<_> + }; + + Ok(self.start_call_raw_async(store, params, lower, lift)?.0) + } + + 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 +421,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, + 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, + |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 +550,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 +629,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 +803,32 @@ impl Func { Ok(()) } + fn lower_args( + cx: &mut LowerContext<'_, T>, + params: &Vec, + params_ty: InterfaceType, + dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>, + ) -> 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 { + Self::store_args(cx, ¶ms_ty, params, dst) + } + } + fn store_args( - &self, cx: &mut LowerContext<'_, T>, params_ty: &TypeTuple, args: &[Val], @@ -662,12 +847,55 @@ 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 { + 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(#4311): 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 +909,157 @@ 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?; + + if !options.async_() { + flags.set_may_enter(false); + flags.set_needs_post_return(true); } + 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, + 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(); + + unsafe { + Ok(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 3ab6581959b8..bba6f1a51503 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(#4311): 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,66 +456,145 @@ 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(()); } -fn validate_inbounds_dynamic(abi: &CanonicalAbiInfo, memory: &[u8], ptr: &ValRaw) -> Result { +pub(crate) fn validate_inbounds_dynamic( + abi: &CanonicalAbiInfo, + memory: &[u8], + ptr: &ValRaw, +) -> Result { // FIXME(#4311): needs memory64 support let ptr = usize::try_from(ptr.get_u32())?; if ptr % usize::try_from(abi.align32)? != 0 { @@ -421,34 +610,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 ff58df0d5277..a5e270873506 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 ecd38faad558..acf7e8a29bc3 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,217 @@ 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)) + #[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, Some(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? + } } - fn call_impl(&self, mut store: impl AsContextMut, params: Params) -> Result { - let store = &mut store.as_context_mut(); + /// 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, Some(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, + 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_raw, + ) + } + } else { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + 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, + ) + } + } + } + #[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_raw, + ) + } + } else { + if Return::flatten_count() <= MAX_FLAT_PARAMS { + self.func.call_raw_async( + store, + params, + Self::lower_heap_args, + Self::lift_stack_result_raw, + ) + } else { + self.func.call_raw_async( + store, + params, + Self::lower_heap_args, + Self::lift_heap_result_raw, + ) + } + }? + .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 +465,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 +499,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 +535,15 @@ 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]) + } + /// See [`Func::post_return`] pub fn post_return(&self, store: impl AsContextMut) -> Result<()> { self.func.post_return(store) @@ -1504,7 +1720,7 @@ pub struct WasmList { } impl WasmList { - fn new( + pub(crate) fn new( ptr: usize, len: usize, cx: &mut LiftContext<'_>, @@ -2456,6 +2672,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..b8d96f969442 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,17 +862,32 @@ impl InstancePre { mut store: impl AsContextMut, ) -> Result where - T: Send, + T: Send + 'static, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "must use sync instantiation when async support is disabled" ); - store.on_fiber(|store| self.instantiate_impl(store)).await? + #[cfg(feature = "component-model-async")] + { + // TODO: do we need to return the store here due to the possible + // invalidation of the reference we were passed? + concurrent::on_fiber(store, None, move |store| self.instantiate_impl(store)) + .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + 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 5c0ed9250164..8b78137d6de2 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/externals/table.rs b/crates/wasmtime/src/runtime/externals/table.rs index 3da105fe4cf8..adbe9bdf9762 100644 --- a/crates/wasmtime/src/runtime/externals/table.rs +++ b/crates/wasmtime/src/runtime/externals/table.rs @@ -95,14 +95,26 @@ impl Table { where T: Send, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "cannot use `new_async` without enabling async support on the config" ); - store - .on_fiber(|store| Table::_new(store.0, ty, init)) + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, move |store| { + Table::_new(store.0, ty, init) + }) .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store + .on_fiber(|store| Table::_new(store.0, ty, init)) + .await? + } } fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result { @@ -289,14 +301,26 @@ impl Table { where T: Send, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "cannot use `grow_async` without enabling async support on the config" ); - store - .on_fiber(|store| self.grow(store, delta, init)) + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, move |store| { + self.grow(store, delta, init) + }) .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store + .on_fiber(|store| self.grow(store, delta, init)) + .await? + } } /// Copy `len` elements from `src_table[src_index..]` into diff --git a/crates/wasmtime/src/runtime/func.rs b/crates/wasmtime/src/runtime/func.rs index edf7264641a3..b74647c6d308 100644 --- a/crates/wasmtime/src/runtime/func.rs +++ b/crates/wasmtime/src/runtime/func.rs @@ -557,16 +557,29 @@ impl Func { ); assert!(ty.comes_from_same_engine(store.as_context().engine())); Func::new(store, ty, move |mut caller, params, results| { - let async_cx = caller - .store - .as_context_mut() - .0 - .async_cx() - .expect("Attempt to spawn new action on dying fiber"); - let mut future = Pin::from(func(caller, params, results)); - 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 caller.store.as_context_mut()); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on::(future.as_mut(), None) } { + Ok((Ok(()), _)) => Ok(()), + Ok((Err(trap), _)) | Err(trap) => Err(trap), + } + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = caller + .store + .as_context_mut() + .0 + .async_cx() + .expect("Attempt to spawn new action on dying fiber"); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(Ok(())) => Ok(()), + Ok(Err(trap)) | Err(trap) => Err(trap), + } } }) } @@ -875,17 +888,31 @@ impl Func { concat!("cannot use `wrap_async` without enabling async support on the config") ); Func::wrap_inner(store, move |mut caller: Caller<'_, T>, args| { - let async_cx = caller - .store - .as_context_mut() - .0 - .async_cx() - .expect("Attempt to start async function on dying fiber"); - let mut future = Pin::from(func(caller, args)); - - match unsafe { async_cx.block_on(future.as_mut()) } { - Ok(ret) => ret.into_fallible(), - Err(e) => R::fallible_from_error(e), + #[cfg(feature = "component-model-async")] + { + let async_cx = + crate::component::concurrent::AsyncCx::new(&mut caller.store.as_context_mut()); + let mut future = Pin::from(func(caller, args)); + + match unsafe { async_cx.block_on::(future.as_mut(), None) } { + Ok((ret, _)) => ret.into_fallible(), + Err(e) => R::fallible_from_error(e), + } + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = caller + .store + .as_context_mut() + .0 + .async_cx() + .expect("Attempt to start async function on dying fiber"); + let mut future = Pin::from(func(caller, args)); + + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(ret) => ret.into_fallible(), + Err(e) => R::fallible_from_error(e), + } } }) } @@ -1154,10 +1181,21 @@ impl Func { if need_gc { store.0.gc_async().await; } - let result = store - .on_fiber(|store| unsafe { self.call_impl_do_call(store, params, results) }) - .await??; - Ok(result) + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, move |store| unsafe { + self.call_impl_do_call(store, params, results) + }) + .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let result = store + .on_fiber(|store| unsafe { self.call_impl_do_call(store, params, results) }) + .await??; + Ok(result) + } } /// Perform dynamic checks that the arguments given to us match @@ -2338,6 +2376,7 @@ impl HostContext { drop(store); let r = func(caller.sub_caller(), params); + if let Err(trap) = caller.store.0.call_hook(CallHook::ReturningFromHost) { break 'ret R::fallible_from_error(trap); } diff --git a/crates/wasmtime/src/runtime/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs index 5c31182e6b2e..8343ed2c721f 100644 --- a/crates/wasmtime/src/runtime/func/typed.rs +++ b/crates/wasmtime/src/runtime/func/typed.rs @@ -132,8 +132,9 @@ where ) -> Result where T: Send, + Results: Send + Sync + 'static, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "must use `call` with non-async stores" @@ -141,12 +142,25 @@ where if Self::need_gc_before_call_raw(store.0, ¶ms) { store.0.gc_async().await; } - store - .on_fiber(|store| { + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, |store| { let func = self.func.vm_func_ref(store.0); unsafe { Self::call_raw(store, &self.ty, func, params) } }) .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store + .on_fiber(|store| { + let func = self.func.vm_func_ref(store.0); + unsafe { Self::call_raw(store, &self.ty, func, params) } + }) + .await? + } } #[inline] diff --git a/crates/wasmtime/src/runtime/instance.rs b/crates/wasmtime/src/runtime/instance.rs index 5e3117fe4383..478476133f51 100644 --- a/crates/wasmtime/src/runtime/instance.rs +++ b/crates/wasmtime/src/runtime/instance.rs @@ -227,9 +227,20 @@ impl Instance { "must use sync instantiation when async support is disabled", ); - store - .on_fiber(|store| Self::new_started_impl(store, module, imports)) + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store.as_context_mut(), None, move |store| { + Self::new_started_impl(store, module, imports) + }) .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + store + .on_fiber(|store| Self::new_started_impl(store, module, imports)) + .await? + } } /// Internal function to create an instance which doesn't have its `start` diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs index 95cbb548447b..1749ed6452c7 100644 --- a/crates/wasmtime/src/runtime/linker.rs +++ b/crates/wasmtime/src/runtime/linker.rs @@ -459,16 +459,29 @@ impl Linker { ); assert!(ty.comes_from_same_engine(self.engine())); self.func_new(module, name, ty, move |mut caller, params, results| { - let async_cx = caller - .store - .as_context_mut() - .0 - .async_cx() - .expect("Attempt to spawn new function on dying fiber"); - let mut future = Pin::from(func(caller, params, results)); - 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 caller.store.as_context_mut()); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on::(future.as_mut(), None) } { + Ok((Ok(()), _)) => Ok(()), + Ok((Err(trap), _)) | Err(trap) => Err(trap), + } + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = caller + .store + .as_context_mut() + .0 + .async_cx() + .expect("Attempt to spawn new function on dying fiber"); + let mut future = Pin::from(func(caller, params, results)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(Ok(())) => Ok(()), + Ok(Err(trap)) | Err(trap) => Err(trap), + } } }) } @@ -562,16 +575,31 @@ impl Linker { let func = HostFunc::wrap_inner( &self.engine, move |mut caller: Caller<'_, T>, args: Params| { - let async_cx = caller - .store - .as_context_mut() - .0 - .async_cx() - .expect("Attempt to start async function on dying fiber"); - let mut future = Pin::from(func(caller, args)); - match unsafe { async_cx.block_on(future.as_mut()) } { - Ok(ret) => ret.into_fallible(), - Err(e) => Args::fallible_from_error(e), + #[cfg(feature = "component-model-async")] + { + let async_cx = crate::component::concurrent::AsyncCx::new( + &mut caller.store.as_context_mut(), + ); + let mut future = Pin::from(func(caller, args)); + + match unsafe { async_cx.block_on::(future.as_mut(), None) } { + Ok((ret, _)) => ret.into_fallible(), + Err(e) => Args::fallible_from_error(e), + } + } + #[cfg(not(feature = "component-model-async"))] + { + let async_cx = caller + .store + .as_context_mut() + .0 + .async_cx() + .expect("Attempt to start async function on dying fiber"); + let mut future = Pin::from(func(caller, args)); + match unsafe { async_cx.block_on(future.as_mut()) } { + Ok(ret) => ret.into_fallible(), + Err(e) => Args::fallible_from_error(e), + } } }, ); diff --git a/crates/wasmtime/src/runtime/memory.rs b/crates/wasmtime/src/runtime/memory.rs index 8ef0b1a51a11..d0d30af8cc6e 100644 --- a/crates/wasmtime/src/runtime/memory.rs +++ b/crates/wasmtime/src/runtime/memory.rs @@ -261,12 +261,24 @@ impl Memory { where T: Send, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "cannot use `new_async` without enabling async support on the config" ); - store.on_fiber(|store| Self::_new(store.0, ty)).await? + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, move |store| { + Self::_new(store.0, ty) + }) + .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store.on_fiber(|store| Self::_new(store.0, ty)).await? + } } /// Helper function for attaching the memory to a "frankenstein" instance @@ -613,12 +625,24 @@ impl Memory { where T: Send, { - let mut store = store.as_context_mut(); + let store = store.as_context_mut(); assert!( store.0.async_support(), "cannot use `grow_async` without enabling async support on the config" ); - store.on_fiber(|store| self.grow(store, delta)).await? + #[cfg(feature = "component-model-async")] + { + crate::component::concurrent::on_fiber(store, None, move |store| { + self.grow(store, delta) + }) + .await? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + let mut store = store; + store.on_fiber(|store| self.grow(store, delta)).await? + } } fn wasmtime_memory(&self, store: &mut StoreOpaque) -> *mut crate::runtime::vm::Memory { diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs index 512e520836ef..a9c91abc57fc 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,47 @@ 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, +} + +impl StoreInner { + /// Yields execution to the caller on out-of-gas or epoch interruption. + /// + /// This only works on async futures and stores, and assumes that we're + /// executing on a fiber. This will yield execution back to the caller once. + #[cfg(feature = "async")] + fn async_yield_impl(&mut self) -> Result<()> { + use crate::runtime::vm::Yield; + + let mut future = Yield::new(); + + // When control returns, we have a `Result<()>` passed + // in from the host fiber. If this finished successfully then + // we were resumed normally via a `poll`, so keep going. If + // the future was dropped while we were yielded, then we need + // to clean up this fiber. Do so by raising a trap which will + // abort all wasm and get caught on the other side to clean + // things up. + #[cfg(feature = "component-model-async")] + unsafe { + let async_cx = + crate::component::concurrent::AsyncCx::new(&mut (&mut *self).as_context_mut()); + async_cx + .block_on( + Pin::new_unchecked(&mut future), + None::>, + )? + .0; + Ok(()) + } + #[cfg(not(feature = "component-model-async"))] + unsafe { + self.async_cx() + .expect("attempted to pull async context during shutdown") + .block_on(Pin::new_unchecked(&mut future)) + } + } } enum ResourceLimiterInner { @@ -408,7 +451,9 @@ struct AsyncState { #[derive(Clone, Copy)] struct PollContext { future_context: *mut Context<'static>, + #[cfg_attr(feature = "component-model-async", allow(dead_code))] guard_range_start: *mut u8, + #[cfg_attr(feature = "component-model-async", allow(dead_code))] guard_range_end: *mut u8, } @@ -592,6 +637,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 +1153,35 @@ 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() + } + + pub(crate) fn async_guard_range(&mut self) -> Range<*mut u8> { + #[cfg(feature = "component-model-async")] + { + self.concurrent_state().async_guard_range() + } + #[cfg(not(feature = "component-model-async"))] + { + #[cfg(feature = "async")] + unsafe { + let ptr = self.0.inner.async_state.current_poll_cx.get(); + (*ptr).guard_range_start..(*ptr).guard_range_end + } + #[cfg(not(feature = "async"))] + { + core::ptr::null_mut()..core::ptr::null_mut() + } + } + } + + #[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 +1267,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() { @@ -1230,14 +1311,33 @@ impl StoreInner { #[cfg(all(feature = "async", feature = "call-hook"))] CallHookInner::Async(handler) => unsafe { - self.inner - .async_cx() - .ok_or_else(|| anyhow!("couldn't grab async_cx for call hook"))? - .block_on( - handler - .handle_call_event((&mut *self).as_context_mut(), s) - .as_mut(), - )? + #[cfg(feature = "component-model-async")] + { + let async_cx = crate::component::concurrent::AsyncCx::try_new( + &mut (&mut *self).as_context_mut(), + ) + .ok_or_else(|| anyhow!("couldn't grab async_cx for call hook"))?; + + async_cx + .block_on( + handler + .handle_call_event((&mut *self).as_context_mut(), s) + .as_mut(), + None::>, + )? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + self.inner + .async_cx() + .ok_or_else(|| anyhow!("couldn't grab async_cx for call hook"))? + .block_on( + handler + .handle_call_event((&mut *self).as_context_mut(), s) + .as_mut(), + )? + } }, CallHookInner::ForceTypeParameterToBeUsed { uninhabited, .. } => { @@ -1890,30 +1990,6 @@ impl StoreOpaque { self.set_fuel(self.get_fuel()?) } - /// Yields execution to the caller on out-of-gas or epoch interruption. - /// - /// This only works on async futures and stores, and assumes that we're - /// executing on a fiber. This will yield execution back to the caller once. - #[cfg(feature = "async")] - fn async_yield_impl(&mut self) -> Result<()> { - use crate::runtime::vm::Yield; - - let mut future = Yield::new(); - - // When control returns, we have a `Result<()>` passed - // in from the host fiber. If this finished successfully then - // we were resumed normally via a `poll`, so keep going. If - // the future was dropped while we were yielded, then we need - // to clean up this fiber. Do so by raising a trap which will - // abort all wasm and get caught on the other side to clean - // things up. - unsafe { - self.async_cx() - .expect("attempted to pull async context during shutdown") - .block_on(Pin::new_unchecked(&mut future)) - } - } - #[inline] pub fn signal_handler(&self) -> Option<*const SignalHandler> { let handler = self.signal_handler.as_ref()?; @@ -2108,18 +2184,6 @@ at https://bytecodealliance.org/security. self.num_component_instances += 1; } - pub(crate) fn async_guard_range(&self) -> Range<*mut u8> { - #[cfg(feature = "async")] - unsafe { - let ptr = self.async_state.current_poll_cx.get(); - (*ptr).guard_range_start..(*ptr).guard_range_end - } - #[cfg(not(feature = "async"))] - { - core::ptr::null_mut()..core::ptr::null_mut() - } - } - #[cfg(feature = "async")] fn allocate_fiber_stack(&mut self) -> Result { if let Some(stack) = self.async_state.last_fiber_stack.take() { @@ -2567,14 +2631,35 @@ unsafe impl crate::runtime::vm::VMStore for StoreInner { } #[cfg(feature = "async")] Some(ResourceLimiterInner::Async(ref mut limiter)) => unsafe { - self.inner - .async_cx() - .expect("ResourceLimiterAsync requires async Store") - .block_on( - limiter(&mut self.data) - .memory_growing(current, desired, maximum) - .as_mut(), - )? + #[cfg(feature = "component-model-async")] + { + _ = limiter; + let async_cx = crate::component::concurrent::AsyncCx::new( + &mut (&mut *self).as_context_mut(), + ); + let Some(ResourceLimiterInner::Async(ref mut limiter)) = self.limiter else { + unreachable!(); + }; + async_cx + .block_on::( + limiter(&mut self.data) + .memory_growing(current, desired, maximum) + .as_mut(), + None, + )? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + self.inner + .async_cx() + .expect("ResourceLimiterAsync requires async Store") + .block_on( + limiter(&mut self.data) + .memory_growing(current, desired, maximum) + .as_mut(), + )? + } }, None => Ok(true), } @@ -2605,7 +2690,7 @@ unsafe impl crate::runtime::vm::VMStore for StoreInner { // Need to borrow async_cx before the mut borrow of the limiter. // self.async_cx() panicks when used with a non-async store, so // wrap this in an option. - #[cfg(feature = "async")] + #[cfg(all(feature = "async", not(feature = "component-model-async")))] let async_cx = if self.async_support() && matches!(self.limiter, Some(ResourceLimiterInner::Async(_))) { @@ -2620,13 +2705,34 @@ unsafe impl crate::runtime::vm::VMStore for StoreInner { } #[cfg(feature = "async")] Some(ResourceLimiterInner::Async(ref mut limiter)) => unsafe { - async_cx - .expect("ResourceLimiterAsync requires async Store") - .block_on( - limiter(&mut self.data) - .table_growing(current, desired, maximum) - .as_mut(), - )? + #[cfg(feature = "component-model-async")] + { + _ = limiter; + let async_cx = crate::component::concurrent::AsyncCx::new( + &mut (&mut *self).as_context_mut(), + ); + let Some(ResourceLimiterInner::Async(ref mut limiter)) = self.limiter else { + unreachable!(); + }; + async_cx + .block_on::( + limiter(&mut self.data) + .table_growing(current, desired, maximum) + .as_mut(), + None, + )? + .0 + } + #[cfg(not(feature = "component-model-async"))] + { + async_cx + .expect("ResourceLimiterAsync requires async Store") + .block_on( + limiter(&mut self.data) + .table_growing(current, desired, maximum) + .as_mut(), + )? + } }, None => Ok(true), } diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index c3ba4655312b..ab4da9ff7d2c 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,182 @@ 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, + post_return: *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 +333,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 +406,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 +439,8 @@ impl ComponentInstance { .unwrap(), ), component_resource_tables, + component_waitable_tables, + component_error_context_tables, runtime_info, resource_types, vmctx: VMComponentContext { @@ -290,6 +515,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 +600,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 +685,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 +768,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 +794,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 +893,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 +969,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 +1096,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 +1159,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 +1205,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/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs index 9e12cdc9716f..859303d4b2c9 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs @@ -357,38 +357,41 @@ where F: FnMut(*mut VMContext, Option>) -> bool, { let caller = store.0.default_caller(); - let result = CallThreadState::new(store.0, caller).with(|cx| match store.0.interpreter() { - // In interpreted mode directly invoke the host closure since we won't - // be using host-based `setjmp`/`longjmp` as that's not going to save - // the context we want. - Some(r) => { - cx.jmp_buf - .set(CallThreadState::JMP_BUF_INTERPRETER_SENTINEL); - closure(caller, Some(r)) - } + let async_guard_range = store.async_guard_range(); + let result = CallThreadState::new(store.0, async_guard_range, caller).with(|cx| { + match store.0.interpreter() { + // In interpreted mode directly invoke the host closure since we won't + // be using host-based `setjmp`/`longjmp` as that's not going to save + // the context we want. + Some(r) => { + cx.jmp_buf + .set(CallThreadState::JMP_BUF_INTERPRETER_SENTINEL); + closure(caller, Some(r)) + } - // In native mode, however, defer to C to do the `setjmp` since Rust - // doesn't understand `setjmp`. - // - // Note that here we pass a function pointer to C to catch longjmp - // within, here it's `call_closure`, and that passes `None` for the - // interpreter since this branch is only ever taken if the interpreter - // isn't present. - None => traphandlers::wasmtime_setjmp( - cx.jmp_buf.as_ptr(), - { - extern "C" fn call_closure(payload: *mut u8, caller: *mut VMContext) -> bool - where - F: FnMut(*mut VMContext, Option>) -> bool, + // In native mode, however, defer to C to do the `setjmp` since Rust + // doesn't understand `setjmp`. + // + // Note that here we pass a function pointer to C to catch longjmp + // within, here it's `call_closure`, and that passes `None` for the + // interpreter since this branch is only ever taken if the interpreter + // isn't present. + None => traphandlers::wasmtime_setjmp( + cx.jmp_buf.as_ptr(), { - unsafe { (*(payload as *mut F))(caller, None) } - } - - call_closure:: - }, - &mut closure as *mut F as *mut u8, - caller, - ), + extern "C" fn call_closure(payload: *mut u8, caller: *mut VMContext) -> bool + where + F: FnMut(*mut VMContext, Option>) -> bool, + { + unsafe { (*(payload as *mut F))(caller, None) } + } + + call_closure:: + }, + &mut closure as *mut F as *mut u8, + caller, + ), + } }); return match result { @@ -458,12 +461,16 @@ mod call_thread_state { pub const JMP_BUF_INTERPRETER_SENTINEL: *mut u8 = 1 as *mut u8; #[inline] - pub(super) fn new(store: &mut StoreOpaque, caller: *mut VMContext) -> CallThreadState { + pub(super) fn new( + store: &mut StoreOpaque, + async_guard_range: Range<*mut u8>, + caller: *mut VMContext, + ) -> CallThreadState { let limits = unsafe { *Instance::from_vmctx(caller, |i| i.runtime_limits()) }; // Don't try to plumb #[cfg] everywhere for this field, just pretend // we're using it on miri/windows to silence compiler warnings. - let _: Range<_> = store.async_guard_range(); + let _: Range<_> = async_guard_range; CallThreadState { unwind: Cell::new(None), @@ -476,7 +483,7 @@ mod call_thread_state { capture_coredump: store.engine().config().coredump_on_trap, limits, #[cfg(all(feature = "signals-based-traps", unix, not(miri)))] - async_guard_range: store.async_guard_range(), + async_guard_range, prev: Cell::new(ptr::null()), old_last_wasm_exit_fp: Cell::new(unsafe { *(*limits).last_wasm_exit_fp.get() }), old_last_wasm_exit_pc: Cell::new(unsafe { *(*limits).last_wasm_exit_pc.get() }), diff --git a/crates/wasmtime/src/runtime/wave/component.rs b/crates/wasmtime/src/runtime/wave/component.rs index 238512012f1c..39c615bfccdf 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-util/src/lib.rs b/crates/wast-util/src/lib.rs index 2dcc8e1537c6..439c90a10fc9 100644 --- a/crates/wast-util/src/lib.rs +++ b/crates/wast-util/src/lib.rs @@ -185,6 +185,7 @@ macro_rules! foreach_config_option { hogs_memory nan_canonicalization component_model_more_flags + component_model_async simd gc_types } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 599cfae1cdd1..fa143250736e 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -21,3 +21,4 @@ log = { workspace = true } [features] component-model = ['wasmtime/component-model'] +component-model-async = ['wasmtime/component-model-async'] 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/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 924bf66d1c40..e9b1ec7575ae 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -94,6 +94,9 @@ pub fn link_component_spectest(linker: &mut component::Linker) -> Result<( use wasmtime::component::{Resource, ResourceType}; let engine = linker.engine().clone(); + linker + .root() + .func_wrap("host-echo-u32", |_, v: (u32,)| Ok(v))?; linker .root() .func_wrap("host-return-two", |_, _: ()| Ok((2u32,)))?; 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 810565acf699..37d548603a5b 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 { - generator.generate_function_trait_sig(func); + generator.generate_function_trait_sig(func, "Data"); Some(mem::take(&mut generator.src).into()) } else { None @@ -474,14 +530,14 @@ impl Wasmtime { .interface_last_seen_as_import .insert(*id, true); generator.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 generator .generator .name_interface(resolve, *id, name, false) @@ -529,8 +585,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] @@ -815,10 +875,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, @@ -845,7 +906,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. /// @@ -875,7 +936,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_}?; @@ -1040,7 +1100,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_} @@ -1099,7 +1159,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); @@ -1366,7 +1431,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)]" @@ -1374,7 +1439,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) { @@ -1384,6 +1449,19 @@ impl Wasmtime { uwrite!(self.src, ": {}", supertraits.join(" + ")); } uwriteln!(self.src, " {{"); + + let has_concurrent_function = self.import_functions.iter().any(|func| { + matches!(func.func.kind, FunctionKind::Freestanding) + && matches!( + self.opts.import_call_style(None, &func.func.name), + CallStyle::Concurrent + ) + }); + + if has_concurrent_function { + self.src.push_str("type Data;\n"); + } + for f in self.import_functions.iter() { if let Some(sig) = &f.sig { self.src.push_str(sig); @@ -1392,23 +1470,31 @@ impl Wasmtime { } uwriteln!(self.src, "}}"); + let get_host_bounds = if let CallStyle::Concurrent = self.opts.call_style() { + let constraints = world_imports_concurrent_constraints(resolve, world, &self.opts); + + format!("{world_camel}Imports{}", constraints("D")) + } else { + format!("{world_camel}Imports") + }; + uwriteln!( self.src, " - pub trait {world_camel}ImportsGetHost: - Fn(T) -> >::Host + pub trait {world_camel}ImportsGetHost: + Fn(T) -> >::Host + Send + Sync + Copy + 'static {{ - type Host: {world_camel}Imports; + type Host: {get_host_bounds}; }} - impl {world_camel}ImportsGetHost for F + impl {world_camel}ImportsGetHost for F where F: Fn(T) -> O + Send + Sync + Copy + 'static, - O: {world_camel}Imports + O: {get_host_bounds}, {{ type Host = O; }} @@ -1416,30 +1502,54 @@ 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 { self.src.push_str(sig); - uwrite!( - self.src, - "{{ {world_camel}Imports::{}(*self,", - rust_function_name(&f.func) - ); + let call_style = self.opts.import_call_style(None, &f.func.name); + if let CallStyle::Concurrent = &call_style { + uwrite!( + self.src, + "{{ <_T as {world_camel}Imports>::{}(store,", + rust_function_name(&f.func) + ); + } else { + uwrite!( + self.src, + "{{ {world_camel}Imports::{}(*self,", + rust_function_name(&f.func) + ); + } for (name, _) in f.func.params.iter() { uwrite!(self.src, "{},", to_rust_ident(name)); } uwrite!(self.src, ")"); - if self.opts.async_.is_import_async(&f.func.name) { + if let CallStyle::Async = &call_style { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); @@ -1452,7 +1562,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(), @@ -1479,7 +1589,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 @@ -1498,20 +1608,36 @@ impl Wasmtime { }; let camel = to_rust_upper_camel_case(&resolve.worlds[world].name); + let data_bounds = if self.opts.is_store_data_send() { - "T: Send," + if let CallStyle::Concurrent = self.opts.call_style() { + "T: Send + 'static," + } else { + "T: Send," + } } else { "" }; let wt = self.wasmtime_path(); if has_world_imports_trait { + let host_bounds = if let CallStyle::Concurrent = self.opts.call_style() { + let constraints = world_imports_concurrent_constraints(resolve, world, &self.opts); + + format!("{camel}Imports{}", constraints("T")) + } else { + format!("{camel}Imports") + }; + uwrite!( self.src, " - pub fn add_to_linker_imports_get_host( + pub fn add_to_linker_imports_get_host< + T, + G: for<'a> {camel}ImportsGetHost<&'a mut T, T, Host: {host_bounds}> + >( linker: &mut {wt}::component::Linker, {options_param} - host_getter: impl for<'a> {camel}ImportsGetHost<&'a mut T>, + host_getter: G, ) -> {wt}::Result<()> where {data_bounds} {{ @@ -1521,6 +1647,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, @@ -1537,7 +1664,53 @@ 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") + ) + }) + .chain(if self.has_world_imports_trait(resolve, world) { + let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name); + let constraints = + world_imports_concurrent_constraints(resolve, world, &self.opts); + Some(format!(" + {world_camel}Imports{}", constraints("T"))) + } else { + None + }) + .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!( @@ -1593,6 +1766,7 @@ impl Wasmtime { } fn generate_add_resource_to_linker( + qualifier: Option<&str>, src: &mut Source, opts: &Opts, wt: &str, @@ -1602,7 +1776,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( @@ -1673,9 +1847,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::ErrorContext => todo!("generate for error-context"), + TypeDefKind::Future(t) => self.type_future(id, name, t.as_ref(), &ty.docs), + TypeDefKind::Stream(t) => self.type_stream(id, name, t, &ty.docs), + TypeDefKind::ErrorContext => self.type_error_context(id, name, &ty.docs), TypeDefKind::Handle(handle) => self.type_handle(id, name, handle, &ty.docs), TypeDefKind::Resource => self.type_resource(id, name, ty, &ty.docs), TypeDefKind::Unknown => unreachable!(), @@ -1722,13 +1896,14 @@ impl<'a> InterfaceGenerator<'a> { } // Generate resource trait - if self.generator.opts.async_.maybe_async() { + if let CallStyle::Async = self.generator.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] @@ -1755,12 +1930,29 @@ impl<'a> InterfaceGenerator<'a> { | FunctionKind::Constructor(resource) => id == resource, }); + let has_concurrent_function = functions.iter().any(|func| { + matches!( + self.generator + .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.generator.opts.async_.is_drop_async(name) { + if let CallStyle::Async = self + .generator + .opts + .drop_call_style(self.qualifier().as_deref(), name) + { uwrite!(self.src, "async "); } uwrite!( @@ -1772,32 +1964,56 @@ impl<'a> InterfaceGenerator<'a> { // Generate impl HostResource for &mut HostResource if !self.generator.opts.skip_mut_forwarding_impls { - let maybe_send = if self.generator.opts.async_.maybe_async() { + let maybe_send = if let CallStyle::Async = self.generator.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 + .generator + .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.generator.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = call_style { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); } - if self.generator.opts.async_.is_drop_async(name) { + if let CallStyle::Async = self + .generator + .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 @@ -2100,10 +2316,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"); @@ -2335,6 +2550,35 @@ impl<'a> InterfaceGenerator<'a> { } } + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + self.rustdoc(docs); + self.push_str(&format!("pub type {name}")); + self.print_generics(None); + self.push_str(" = "); + self.print_stream(ty); + self.push_str(";\n"); + self.assert_type(id, &name); + } + + fn type_future(&mut self, id: TypeId, name: &str, ty: Option<&Type>, docs: &Docs) { + self.rustdoc(docs); + self.push_str(&format!("pub type {name}")); + self.print_generics(None); + self.push_str(" = "); + self.print_future(ty); + self.push_str(";\n"); + self.assert_type(id, &name); + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + self.rustdoc(docs); + self.push_str(&format!("pub type {name}")); + self.push_str(" = "); + self.print_error_context(); + self.push_str(";\n"); + self.assert_type(id, &name); + } + fn print_result_ty(&mut self, results: &Results, mode: TypeMode) { match results { Results::Named(rs) => match rs.len() { @@ -2355,6 +2599,24 @@ 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 (_, ty) in rs { + self.print_ty(ty, mode); + self.push_str(", "); + } + self.push_str(")"); + } + Results::Anon(ty) => { + self.print_ty(ty, mode); + self.push_str(",)"); + } + } + } + fn special_case_trappable_error( &mut self, func: &Function, @@ -2397,7 +2659,7 @@ impl<'a> InterfaceGenerator<'a> { let owner = TypeOwner::Interface(id); let wt = self.generator.wasmtime_path(); - let is_maybe_async = self.generator.opts.async_.maybe_async(); + let is_maybe_async = matches!(self.generator.opts.call_style(), CallStyle::Async); if is_maybe_async { uwriteln!( self.src, @@ -2407,24 +2669,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.generator + .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"); } @@ -2472,13 +2755,33 @@ impl<'a> InterfaceGenerator<'a> { } uwriteln!(self.src, "}}"); - let (data_bounds, mut host_bounds) = if self.generator.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.generator.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.generator.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.generator.interface_link_options[&id].has_any() { @@ -2490,28 +2793,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} {{ @@ -2522,6 +2825,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.generator.opts, &wt, @@ -2559,23 +2863,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 + .generator + .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.generator.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = call_style { uwrite!(self.src, ".await"); } uwriteln!(self.src, "}}"); @@ -2595,15 +2922,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.generator.opts.async_.is_import_async(&func.name) { - "func_wrap_async" - } else { - "func_wrap" + match self + .generator + .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 ); @@ -2627,16 +2963,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 + .generator + .opts + .import_call_style(self.qualifier().as_deref(), &func.name); if self.generator.opts.tracing { - if self.generator.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = style { self.src.push_str("use tracing::Instrument;\n"); } @@ -2662,7 +3002,7 @@ impl<'a> InterfaceGenerator<'a> { ); } - if self.generator.opts.async_.is_import_async(&func.name) { + if let CallStyle::Async = &style { uwriteln!( self.src, " {wt}::component::__internal::Box::new(async move {{ " @@ -2694,8 +3034,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 { @@ -2714,15 +3057,33 @@ 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.generator.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.generator.opts.tracing { @@ -2764,29 +3125,53 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, "r\n"); } - if self.generator.opts.async_.is_import_async(&func.name) { - // Need to close Box::new and async block - - if self.generator.opts.tracing { - self.src.push_str("}.instrument(span))\n"); - } else { - self.src.push_str("})\n"); + match &style { + CallStyle::Sync => (), + CallStyle::Async => { + if self.generator.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.generator.wasmtime_path(); self.rustdoc(&func.docs); - if self.generator.opts.async_.is_import_async(&func.name) { + let style = self + .generator + .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); @@ -2797,6 +3182,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.generator.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) { @@ -2819,6 +3208,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) { @@ -2849,12 +3242,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.generator.opts.async_.maybe_async(); - - let (async_, async__, await_) = if is_async { - ("async", "_async", ".await") - } else { - ("", "", "") + let style = self.generator.opts.call_style(); + let (async_, async__, await_, concurrent) = match &style { + CallStyle::Async | CallStyle::Concurrent => { + if self.generator.opts.concurrent_exports { + ("async", "INVALID", "INVALID", true) + } else { + ("async", "_async", ".await", false) + } + } + CallStyle::Sync => ("", "", "", false), }; self.rustdoc(&func.docs); @@ -2866,23 +3263,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.generator.opts.tracing { - if is_async { + uwrite!( + self.src, + "> where ::Data: Send + 'static {{\n" + ); + + // TODO: support tracing concurrent calls + if self.generator.opts.tracing && !concurrent { + if let CallStyle::Async = &style { self.src.push_str("use tracing::Instrument;\n"); } @@ -2902,7 +3311,7 @@ impl<'a> InterfaceGenerator<'a> { func.name, )); - if !is_async { + if !matches!(&style, CallStyle::Async) { self.src.push_str( " let _enter = span.enter(); @@ -2914,7 +3323,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("), ("); @@ -2932,46 +3341,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.generator.opts.tracing { - ".instrument(span.clone())" - } else { - "" - }; - uwriteln!(self.src, ")){instrument}{await_}?;"); - - let instrument = if is_async && self.generator.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.generator.opts.tracing { + ".instrument(span.clone())" + } else { + "" + }; + uwriteln!(self.src, ")){instrument}{await_}?;"); + + let instrument = if matches!(&style, CallStyle::Async) && self.generator.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"); @@ -3250,10 +3678,12 @@ fn type_contains_lists(ty: Type, resolve: &Resolve) -> bool { Type::Id(id) => match &resolve.types[id].kind { TypeDefKind::Resource | TypeDefKind::Unknown - | TypeDefKind::ErrorContext | TypeDefKind::Flags(_) | TypeDefKind::Handle(_) - | TypeDefKind::Enum(_) => false, + | TypeDefKind::Enum(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Future(_) + | TypeDefKind::ErrorContext => false, TypeDefKind::Option(ty) => type_contains_lists(*ty, resolve), TypeDefKind::Result(Result_ { ok, err }) => { option_type_contains_lists(*ok, resolve) @@ -3272,8 +3702,6 @@ fn type_contains_lists(ty: Type, resolve: &Resolve) -> bool { .iter() .any(|case| option_type_contains_lists(case.ty, resolve)), TypeDefKind::Type(ty) => type_contains_lists(*ty, resolve), - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), TypeDefKind::List(_) => true, }, @@ -3365,3 +3793,130 @@ fn get_world_resources<'a>( _ => None, }) } + +fn concurrent_constraints<'a>( + resolve: &'a Resolve, + opts: &Opts, + qualifier: Option<&str>, + id: InterfaceId, +) -> impl Fn(&str) -> String + use<'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(", ") + ) + } + } +} + +fn world_imports_concurrent_constraints<'a>( + resolve: &'a Resolve, + world: WorldId, + opts: &Opts, +) -> impl Fn(&str) -> String + use<'a> { + let has_concurrent_function = resolve.worlds[world] + .imports + .values() + .any(|item| match item { + WorldItem::Function(func) => { + matches!(func.kind, FunctionKind::Freestanding) + && matches!( + opts.import_call_style(None, &func.name), + CallStyle::Concurrent + ) + } + WorldItem::Interface { .. } | WorldItem::Type(_) => false, + }); + + let types = resolve.worlds[world] + .imports + .iter() + .filter_map(|(name, item)| match (name, item) { + (WorldKey::Name(name), WorldItem::Type(ty)) => match resolve.types[*ty].kind { + TypeDefKind::Resource + if resolve.worlds[world] + .imports + .values() + .any(|item| match item { + WorldItem::Function(func) => match func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(resource) + | FunctionKind::Static(resource) + | FunctionKind::Constructor(resource) => { + *ty == resource + && matches!( + opts.import_call_style(None, &func.name), + CallStyle::Concurrent + ) + } + }, + WorldItem::Interface { .. } | WorldItem::Type(_) => false, + }) => + { + Some(format!("{}Data", name.to_upper_camel_case())) + } + _ => None, + }, + _ => 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 7b40523be26d..002b857594e8 100644 --- a/crates/wit-bindgen/src/rust.rs +++ b/crates/wit-bindgen/src/rust.rs @@ -115,13 +115,12 @@ pub trait RustGenerator<'a> { | TypeDefKind::Enum(_) | TypeDefKind::Tuple(_) | TypeDefKind::Handle(_) - | TypeDefKind::Resource - | TypeDefKind::ErrorContext => true, + | TypeDefKind::Resource => true, TypeDefKind::Type(Type::Id(t)) => { needs_generics(resolve, &resolve.types[*t].kind) } TypeDefKind::Type(Type::String) => true, - TypeDefKind::Type(_) => false, + TypeDefKind::Type(_) | TypeDefKind::ErrorContext => false, TypeDefKind::Unknown => unreachable!(), } } @@ -166,10 +165,19 @@ pub trait RustGenerator<'a> { TypeDefKind::Enum(_) => { panic!("unsupported anonymous type reference: enum") } - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::ErrorContext => todo!(), - + TypeDefKind::Future(ty) => { + self.push_str("wasmtime::component::FutureReader<"); + self.print_optional_ty(ty.as_ref(), TypeMode::Owned); + self.push_str(">"); + } + 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); } @@ -216,6 +224,25 @@ pub trait RustGenerator<'a> { } } + fn print_stream(&mut self, ty: &Type) { + let wt = self.wasmtime_path(); + self.push_str(&format!("{wt}::component::StreamReader<")); + self.print_ty(ty, TypeMode::Owned); + self.push_str(">"); + } + + fn print_future(&mut self, ty: Option<&Type>) { + let wt = self.wasmtime_path(); + self.push_str(&format!("{wt}::component::FutureReader<")); + self.print_optional_ty(ty, TypeMode::Owned); + self.push_str(">"); + } + + fn print_error_context(&mut self) { + let wt = self.wasmtime_path(); + self.push_str(&format!("{wt}::component::ErrorContext")); + } + fn print_handle(&mut self, handle: &Handle) { // Handles are either printed as `ResourceAny` for any guest-defined // resource or `Resource` for all host-defined resources. This means diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs index 6cb388d4bd16..b29c0da728c1 100644 --- a/crates/wit-bindgen/src/types.rs +++ b/crates/wit-bindgen/src/types.rs @@ -148,21 +148,18 @@ 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) => { info = self.optional_type_info(resolve, r.ok.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref()); } - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::ErrorContext => todo!(), + TypeDefKind::Future(ty) => { + info = self.optional_type_info(resolve, ty.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/bindgen.rs b/tests/all/component_model/bindgen.rs index e89b04f0ca09..9343a6a24152 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -5,7 +5,7 @@ use super::engine; use anyhow::Result; use wasmtime::{ component::{Component, Linker}, - Store, + Config, Engine, Store, }; mod ownership; @@ -58,6 +58,73 @@ mod no_imports { } } +mod no_imports_concurrent { + use super::*; + use wasmtime::component::PromisesUnordered; + + wasmtime::component::bindgen!({ + inline: " + package foo:foo; + + world no-imports { + export foo: interface { + foo: func(); + } + + export bar: func(); + } + ", + async: true, + concurrent_exports: true, + }); + + #[tokio::test] + async fn run() -> Result<()> { + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; + + let component = Component::new( + &engine, + r#" + (component + (core module $m + (import "" "task.return" (func $task-return)) + (func (export "bar") (result i32) + call $task-return + i32.const 0 + ) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core type $task-return-type (func)) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) + + (func $f (export "bar") + (canon lift (core func $i "bar") async (callback (func $i "callback"))) + ) + + (instance $i (export "foo" (func $f))) + (export "foo" (instance $i)) + ) + "#, + )?; + + let linker = Linker::new(&engine); + let mut store = Store::new(&engine, ()); + let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?; + let mut promises = PromisesUnordered::new(); + promises.push(no_imports.call_bar(&mut store).await?); + promises.push(no_imports.foo().call_foo(&mut store).await?); + assert!(promises.next(&mut store).await?.is_some()); + assert!(promises.next(&mut store).await?.is_some()); + Ok(()) + } +} + mod one_import { use super::*; @@ -121,6 +188,111 @@ mod one_import { } } +mod one_import_concurrent { + use { + super::*, + std::future::Future, + wasmtime::{component, StoreContextMut}, + }; + + wasmtime::component::bindgen!({ + inline: " + package foo:foo; + + world no-imports { + import foo: interface { + foo: func(); + } + + export bar: func(); + } + ", + async: true, + concurrent_imports: true, + concurrent_exports: true, + }); + + #[tokio::test] + async fn run() -> Result<()> { + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; + + let component = Component::new( + &engine, + r#" + (component + (import "foo" (instance $foo-instance + (export "foo" (func)) + )) + (core module $libc + (memory (export "memory") 1) + ) + (core instance $libc-instance (instantiate $libc)) + (core module $m + (import "" "foo" (func $foo (param i32 i32) (result i32))) + (import "" "task.return" (func $task-return)) + (func (export "bar") (result i32) + i32.const 0 + i32.const 0 + call $foo + drop + call $task-return + i32.const 0 + ) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core func $foo (canon lower (func $foo-instance "foo") async (memory $libc-instance "memory"))) + (core type $task-return-type (func)) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance + (export "task.return" (func $task-return)) + (export "foo" (func $foo)) + )) + )) + + (func $f (export "bar") + (canon lift (core func $i "bar") async (callback (func $i "callback"))) + ) + + (instance $i (export "foo" (func $f))) + (export "foo" (instance $i)) + ) + "#, + )?; + + #[derive(Default)] + struct MyImports { + hit: bool, + } + + impl foo::Host for MyImports { + type Data = MyImports; + + fn foo( + mut store: StoreContextMut<'_, Self::Data>, + ) -> impl Future) + 'static> + + Send + + Sync + + 'static { + store.data_mut().hit = true; + async { component::for_any(|_| ()) } + } + } + + let mut linker = Linker::new(&engine); + foo::add_to_linker(&mut linker, |f: &mut MyImports| f)?; + let mut store = Store::new(&engine, MyImports::default()); + let no_imports = NoImports::instantiate_async(&mut store, &component, &linker).await?; + let promise = no_imports.call_bar(&mut store).await?; + promise.get(&mut store).await?; + assert!(store.data().hit); + Ok(()) + } +} + mod resources_at_world_level { use super::*; use wasmtime::component::Resource; diff --git a/tests/all/component_model/call_hook.rs b/tests/all/component_model/call_hook.rs index 91f71151aa48..5064a6b7f3d6 100644 --- a/tests/all/component_model/call_hook.rs +++ b/tests/all/component_model/call_hook.rs @@ -610,12 +610,15 @@ async fn drop_suspended_async_hook() -> Result<()> { times: u32, } - impl Future for PollNTimes { + impl Future for PollNTimes + where + F::Output: std::fmt::Debug, + { type Output = (); fn poll(mut self: Pin<&mut Self>, task: &mut task::Context<'_>) -> Poll<()> { for i in 0..self.times { match Pin::new(&mut self.future).poll(task) { - Poll::Ready(_) => panic!("future should not be ready at {i}"), + Poll::Ready(v) => panic!("future should not be ready at {i}; result is {v:?}"), Poll::Pending => {} } } 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/component_model/func.rs b/tests/all/component_model/func.rs index 2632a830b348..5550cd1fd912 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -821,13 +821,109 @@ fn strings() -> Result<()> { Ok(()) } -#[test] -fn many_parameters() -> Result<()> { - let component = format!( +#[tokio::test] +async fn missing_task_return_call_stackless() -> Result<()> { + test_missing_task_return_call(r#"(component + (core module $m + (import "" "task.return" (func $task-return)) + (func (export "foo") (result i32) + i32.const 0 + ) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core type $task-return-type (func)) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) + (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback")))) + )"#).await +} + +#[tokio::test] +async fn missing_task_return_call_stackful() -> Result<()> { + test_missing_task_return_call( r#"(component (core module $m - (memory (export "memory") 1) - (func (export "foo") (param i32) (result i32) + (import "" "task.return" (func $task-return)) + (func (export "foo")) + ) + (core type $task-return-type (func)) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) + (func (export "foo") (canon lift (core func $i "foo") async)) + )"#, + ) + .await +} + +async fn test_missing_task_return_call(component: &str) -> Result<()> { + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; + let component = Component::new(&engine, component)?; + let mut store = Store::new(&engine, ()); + + let instance = Linker::new(&engine) + .instantiate_async(&mut store, &component) + .await?; + + let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?; + + match func.call_concurrent(&mut store, ()).await { + Ok(_) => panic!(), + Err(e) => assert_eq!( + "wasm trap: async-lifted export failed to produce a result", + &format!("{e:?}") + ), + } + + Ok(()) +} + +#[tokio::test] +async fn many_parameters() -> Result<()> { + test_many_parameters(false, false).await +} + +#[tokio::test] +async fn many_parameters_concurrent() -> Result<()> { + test_many_parameters(false, true).await +} + +#[tokio::test] +async fn many_parameters_dynamic() -> Result<()> { + test_many_parameters(true, false).await +} + +#[tokio::test] +async fn many_parameters_dynamic_concurrent() -> Result<()> { + test_many_parameters(true, true).await +} + +async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> { + let (body, async_opts) = if concurrent { + ( + r#" + (call $task-return + (i32.const 0) + (i32.mul + (memory.size) + (i32.const 65536) + ) + (local.get 0) + ) + + (i32.const 0) + "#, + r#"async (callback (func $i "callback"))"#, + ) + } else { + ( + r#" (local $base i32) ;; Allocate space for the return @@ -855,11 +951,28 @@ fn many_parameters() -> Result<()> { (local.get 0)) (local.get $base) + "#, + "", + ) + }; + + let component = format!( + r#"(component + (core module $m + (import "" "task.return" (func $task-return (param i32 i32 i32))) + (memory (export "memory") 1) + (func (export "foo") (param i32) (result i32) + {body} ) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) {REALLOC_AND_FREE} ) - (core instance $i (instantiate $m)) + (core type $task-return-type (func (param i32 i32 i32))) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) (type $t (func (param "p1" s8) ;; offset 0, size 1 @@ -870,11 +983,11 @@ fn many_parameters() -> Result<()> { (param "p6" string) ;; offset 24, size 8 (param "p7" (list u32)) ;; offset 32, size 8 (param "p8" bool) ;; offset 40, size 1 - (param "p0" bool) ;; offset 40, size 1 - (param "pa" char) ;; offset 44, size 4 - (param "pb" (list bool)) ;; offset 48, size 8 - (param "pc" (list char)) ;; offset 56, size 8 - (param "pd" (list string)) ;; offset 64, size 8 + (param "p9" bool) ;; offset 41, size 1 + (param "p0" char) ;; offset 44, size 4 + (param "pa" (list bool)) ;; offset 48, size 8 + (param "pb" (list char)) ;; offset 56, size 8 + (param "pc" (list string)) ;; offset 64, size 8 (result (tuple (list u8) u32)) )) @@ -883,30 +996,22 @@ fn many_parameters() -> Result<()> { (core func $i "foo") (memory $i "memory") (realloc (func $i "realloc")) + {async_opts} ) ) )"# ); - let engine = super::engine(); + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); - let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<( - i8, - u64, - f32, - u8, - i16, - &str, - &[u32], - bool, - bool, - char, - &[bool], - &[char], - &[&str], - ), ((WasmList, u32),)>(&mut store, "many-param")?; + + let instance = Linker::new(&engine) + .instantiate_async(&mut store, &component) + .await?; let input = ( -100, @@ -930,8 +1035,76 @@ fn many_parameters() -> Result<()> { ] .as_slice(), ); - let ((memory, pointer),) = func.call(&mut store, input)?; - let memory = memory.as_le_slice(&store); + + let (memory, pointer) = if dynamic { + let input = vec![ + Val::S8(input.0), + Val::U64(input.1), + Val::Float32(input.2), + Val::U8(input.3), + Val::S16(input.4), + Val::String(input.5.into()), + Val::List(input.6.iter().copied().map(Val::U32).collect()), + Val::Bool(input.7), + Val::Bool(input.8), + Val::Char(input.9), + Val::List(input.10.iter().copied().map(Val::Bool).collect()), + Val::List(input.11.iter().copied().map(Val::Char).collect()), + Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()), + ]; + let func = instance.get_func(&mut store, "many-param").unwrap(); + + let mut results = if concurrent { + let promise = func.call_concurrent(&mut store, input).await?; + promise.get(&mut store).await?.into_iter() + } else { + let mut results = vec![Val::Bool(false)]; + func.call_async(&mut store, &input, &mut results).await?; + results.into_iter() + }; + + let Some(Val::Tuple(results)) = results.next() else { + panic!() + }; + let mut results = results.into_iter(); + let Some(Val::List(memory)) = results.next() else { + panic!() + }; + let Some(Val::U32(pointer)) = results.next() else { + panic!() + }; + ( + memory + .into_iter() + .map(|v| if let Val::U8(v) = v { v } else { panic!() }) + .collect(), + pointer, + ) + } else { + let func = instance.get_typed_func::<( + i8, + u64, + f32, + u8, + i16, + &str, + &[u32], + bool, + bool, + char, + &[bool], + &[char], + &[&str], + ), ((Vec, u32),)>(&mut store, "many-param")?; + + if concurrent { + let promise = func.call_concurrent(&mut store, input).await?; + promise.get(&mut store).await?.0 + } else { + func.call_async(&mut store, input).await?.0 + } + }; + let memory = &memory[..]; let mut actual = &memory[pointer as usize..][..72]; assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0); @@ -981,6 +1154,437 @@ fn many_parameters() -> Result<()> { Ok(()) } +#[tokio::test] +async fn many_results() -> Result<()> { + test_many_results(false, false).await +} + +#[tokio::test] +async fn many_results_concurrent() -> Result<()> { + test_many_results(false, true).await +} + +#[tokio::test] +async fn many_results_dynamic() -> Result<()> { + test_many_results(true, false).await +} + +#[tokio::test] +async fn many_results_dynamic_concurrent() -> Result<()> { + test_many_results(true, true).await +} + +async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> { + let (ret, async_opts) = if concurrent { + ( + r#" + call $task-return + i32.const 0 + "#, + r#"async (callback (func $i "callback"))"#, + ) + } else { + ("", "") + }; + + let my_nan = CANON_32BIT_NAN | 1; + + let component = format!( + r#"(component + (core module $m + (import "" "task.return" (func $task-return (param i32))) + (memory (export "memory") 1) + (func (export "foo") (result i32) + (local $base i32) + (local $string i32) + (local $list i32) + + (local.set $base + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 8) + (i32.const 72))) + + (i32.store8 offset=0 + (local.get $base) + (i32.const -100)) + + (i64.store offset=8 + (local.get $base) + (i64.const 9223372036854775807)) + + (f32.store offset=16 + (local.get $base) + (f32.reinterpret_i32 (i32.const {my_nan}))) + + (i32.store8 offset=20 + (local.get $base) + (i32.const 38)) + + (i32.store16 offset=22 + (local.get $base) + (i32.const 18831)) + + (local.set $string + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 1) + (i32.const 6))) + + (i32.store8 offset=0 + (local.get $string) + (i32.const 97)) ;; 'a' + (i32.store8 offset=1 + (local.get $string) + (i32.const 98)) ;; 'b' + (i32.store8 offset=2 + (local.get $string) + (i32.const 99)) ;; 'c' + (i32.store8 offset=3 + (local.get $string) + (i32.const 100)) ;; 'd' + (i32.store8 offset=4 + (local.get $string) + (i32.const 101)) ;; 'e' + (i32.store8 offset=5 + (local.get $string) + (i32.const 102)) ;; 'f' + + (i32.store offset=24 + (local.get $base) + (local.get $string)) + + (i32.store offset=28 + (local.get $base) + (i32.const 2)) + + (local.set $list + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 4) + (i32.const 32))) + + (i32.store offset=0 + (local.get $list) + (i32.const 1)) + (i32.store offset=4 + (local.get $list) + (i32.const 2)) + (i32.store offset=8 + (local.get $list) + (i32.const 3)) + (i32.store offset=12 + (local.get $list) + (i32.const 4)) + (i32.store offset=16 + (local.get $list) + (i32.const 5)) + (i32.store offset=20 + (local.get $list) + (i32.const 6)) + (i32.store offset=24 + (local.get $list) + (i32.const 7)) + (i32.store offset=28 + (local.get $list) + (i32.const 8)) + + (i32.store offset=32 + (local.get $base) + (local.get $list)) + + (i32.store offset=36 + (local.get $base) + (i32.const 8)) + + (i32.store8 offset=40 + (local.get $base) + (i32.const 1)) + + (i32.store8 offset=41 + (local.get $base) + (i32.const 0)) + + (i32.store offset=44 + (local.get $base) + (i32.const 128681)) ;; '🚩' + + (local.set $list + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 1) + (i32.const 5))) + + (i32.store8 offset=0 + (local.get $list) + (i32.const 0)) + (i32.store8 offset=1 + (local.get $list) + (i32.const 1)) + (i32.store8 offset=2 + (local.get $list) + (i32.const 0)) + (i32.store8 offset=3 + (local.get $list) + (i32.const 1)) + (i32.store8 offset=4 + (local.get $list) + (i32.const 1)) + + (i32.store offset=48 + (local.get $base) + (local.get $list)) + + (i32.store offset=52 + (local.get $base) + (i32.const 5)) + + (local.set $list + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 4) + (i32.const 20))) + + (i32.store offset=0 + (local.get $list) + (i32.const 127820)) ;; '🍌' + (i32.store offset=4 + (local.get $list) + (i32.const 129360)) ;; '🥐' + (i32.store offset=8 + (local.get $list) + (i32.const 127831)) ;; '🍗' + (i32.store offset=12 + (local.get $list) + (i32.const 127833)) ;; '🍙' + (i32.store offset=16 + (local.get $list) + (i32.const 127841)) ;; '🍡' + + (i32.store offset=56 + (local.get $base) + (local.get $list)) + + (i32.store offset=60 + (local.get $base) + (i32.const 5)) + + (local.set $list + (call $realloc + (i32.const 0) + (i32.const 0) + (i32.const 4) + (i32.const 16))) + + (i32.store offset=0 + (local.get $list) + (i32.add (local.get $string) (i32.const 2))) + (i32.store offset=4 + (local.get $list) + (i32.const 2)) + (i32.store offset=8 + (local.get $list) + (i32.add (local.get $string) (i32.const 4))) + (i32.store offset=12 + (local.get $list) + (i32.const 2)) + + (i32.store offset=64 + (local.get $base) + (local.get $list)) + + (i32.store offset=68 + (local.get $base) + (i32.const 2)) + + local.get $base + + {ret} + ) + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + + {REALLOC_AND_FREE} + ) + (core type $task-return-type (func (param i32))) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) + + (type $t (func (result (tuple + s8 + u64 + float32 + u8 + s16 + string + (list u32) + bool + bool + char + (list bool) + (list char) + (list string) + )))) + (func (export "many-results") (type $t) + (canon lift + (core func $i "foo") + (memory $i "memory") + (realloc (func $i "realloc")) + {async_opts} + ) + ) + )"# + ); + + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; + let component = Component::new(&engine, component)?; + let mut store = Store::new(&engine, ()); + + let instance = Linker::new(&engine) + .instantiate_async(&mut store, &component) + .await?; + + let expected = ( + -100i8, + u64::MAX / 2, + f32::from_bits(CANON_32BIT_NAN | 1), + 38u8, + 18831i16, + "ab".to_string(), + vec![1u32, 2, 3, 4, 5, 6, 7, 8], + true, + false, + '🚩', + vec![false, true, false, true, true], + vec!['🍌', '🥐', '🍗', '🍙', '🍡'], + vec!["cd".to_string(), "ef".to_string()], + ); + + let actual = if dynamic { + let func = instance.get_func(&mut store, "many-results").unwrap(); + + let mut results = if concurrent { + let promise = func.call_concurrent(&mut store, Vec::new()).await?; + promise.get(&mut store).await?.into_iter() + } else { + let mut results = vec![Val::Bool(false)]; + func.call_async(&mut store, &[], &mut results).await?; + results.into_iter() + }; + + let Some(Val::Tuple(results)) = results.next() else { + panic!() + }; + let mut results = results.into_iter(); + let Some(Val::S8(p1)) = results.next() else { + panic!() + }; + let Some(Val::U64(p2)) = results.next() else { + panic!() + }; + let Some(Val::Float32(p3)) = results.next() else { + panic!() + }; + let Some(Val::U8(p4)) = results.next() else { + panic!() + }; + let Some(Val::S16(p5)) = results.next() else { + panic!() + }; + let Some(Val::String(p6)) = results.next() else { + panic!() + }; + let Some(Val::List(p7)) = results.next() else { + panic!() + }; + let p7 = p7 + .into_iter() + .map(|v| if let Val::U32(v) = v { v } else { panic!() }) + .collect(); + let Some(Val::Bool(p8)) = results.next() else { + panic!() + }; + let Some(Val::Bool(p9)) = results.next() else { + panic!() + }; + let Some(Val::Char(p0)) = results.next() else { + panic!() + }; + let Some(Val::List(pa)) = results.next() else { + panic!() + }; + let pa = pa + .into_iter() + .map(|v| if let Val::Bool(v) = v { v } else { panic!() }) + .collect(); + let Some(Val::List(pb)) = results.next() else { + panic!() + }; + let pb = pb + .into_iter() + .map(|v| if let Val::Char(v) = v { v } else { panic!() }) + .collect(); + let Some(Val::List(pc)) = results.next() else { + panic!() + }; + let pc = pc + .into_iter() + .map(|v| if let Val::String(v) = v { v } else { panic!() }) + .collect(); + + (p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc) + } else { + let func = instance.get_typed_func::<(), (( + i8, + u64, + f32, + u8, + i16, + String, + Vec, + bool, + bool, + char, + Vec, + Vec, + Vec, + ),)>(&mut store, "many-results")?; + + if concurrent { + let promise = func.call_concurrent(&mut store, ()).await?; + promise.get(&mut store).await?.0 + } else { + func.call_async(&mut store, ()).await?.0 + } + }; + + assert_eq!(expected.0, actual.0); + assert_eq!(expected.1, actual.1); + assert!(expected.2.is_nan()); + assert!(actual.2.is_nan()); + assert_eq!(expected.3, actual.3); + assert_eq!(expected.4, actual.4); + assert_eq!(expected.5, actual.5); + assert_eq!(expected.6, actual.6); + assert_eq!(expected.7, actual.7); + assert_eq!(expected.8, actual.8); + assert_eq!(expected.9, actual.9); + assert_eq!(expected.10, actual.10); + assert_eq!(expected.11, actual.11); + assert_eq!(expected.12, actual.12); + + Ok(()) +} + #[test] fn some_traps() -> Result<()> { let middle_of_memory = (i32::MAX / 2) & (!0xff); diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 2dfd6a37ce09..abb037d5ce1d 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -3,8 +3,9 @@ use super::REALLOC_AND_FREE; use anyhow::Result; use std::ops::Deref; +use wasmtime::component; use wasmtime::component::*; -use wasmtime::{Store, StoreContextMut, Trap, WasmBacktrace}; +use wasmtime::{Config, Engine, Store, StoreContextMut, Trap, WasmBacktrace}; #[test] fn can_compile() -> Result<()> { @@ -481,37 +482,86 @@ fn attempt_to_reenter_during_host() -> Result<()> { Ok(()) } -#[test] -fn stack_and_heap_args_and_rets() -> Result<()> { - let component = format!( - r#" -(component - (type $many_params (tuple - string string string string - string string string string - string)) - (import "f1" (func $f1 (param "a" u32) (result u32))) - (import "f2" (func $f2 (param "a" $many_params) (result u32))) - (import "f3" (func $f3 (param "a" u32) (result string))) - (import "f4" (func $f4 (param "a" $many_params) (result string))) +#[tokio::test] +async fn stack_and_heap_args_and_rets() -> Result<()> { + test_stack_and_heap_args_and_rets(false).await +} - (core module $libc - {REALLOC_AND_FREE} - (memory (export "memory") 1) - ) - (core instance $libc (instantiate (module $libc))) +#[tokio::test] +async fn stack_and_heap_args_and_rets_concurrent() -> Result<()> { + test_stack_and_heap_args_and_rets(true).await +} - (core func $f1_lower (canon lower (func $f1) (memory $libc "memory") (realloc (func $libc "realloc")))) - (core func $f2_lower (canon lower (func $f2) (memory $libc "memory") (realloc (func $libc "realloc")))) - (core func $f3_lower (canon lower (func $f3) (memory $libc "memory") (realloc (func $libc "realloc")))) - (core func $f4_lower (canon lower (func $f4) (memory $libc "memory") (realloc (func $libc "realloc")))) +async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { + let (body, async_lower_opts, async_lift_opts) = if concurrent { + ( + r#" + (import "host" "f1" (func $f1 (param i32 i32) (result i32))) + (import "host" "f2" (func $f2 (param i32 i32) (result i32))) + (import "host" "f3" (func $f3 (param i32 i32) (result i32))) + (import "host" "f4" (func $f4 (param i32 i32) (result i32))) - (core module $m + (func $run (export "run") (result i32) + (local $params i32) + (local $results i32) + + block + (local.set $params (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) + (i32.store offset=0 (local.get $params) (i32.const 1)) + (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) + (call $f1 (local.get $params) (local.get $results)) + drop + (i32.load offset=0 (local.get $results)) + i32.const 2 + i32.eq + br_if 0 + unreachable + end + + block + (local.set $params (call $allocate_empty_strings)) + (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) + (call $f2 (local.get $params) (local.get $results)) + drop + (i32.load offset=0 (local.get $results)) + i32.const 3 + i32.eq + br_if 0 + unreachable + end + + block + (local.set $params (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) + (i32.store offset=0 (local.get $params) (i32.const 8)) + (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $f3 (local.get $params) (local.get $results)) + drop + (call $validate_string_ret (local.get $results)) + end + + block + (local.set $params (call $allocate_empty_strings)) + (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) + (call $f4 (local.get $params) (local.get $results)) + drop + (call $validate_string_ret (local.get $results)) + end + + (call $task-return) + + i32.const 0 + ) + "#, + "async", + r#"async (callback (func $m "callback"))"#, + ) + } else { + ( + r#" (import "host" "f1" (func $f1 (param i32) (result i32))) (import "host" "f2" (func $f2 (param i32) (result i32))) (import "host" "f3" (func $f3 (param i32 i32))) (import "host" "f4" (func $f4 (param i32 i32))) - (import "libc" "memory" (memory 1)) (func $run (export "run") block @@ -546,6 +596,58 @@ fn stack_and_heap_args_and_rets() -> Result<()> { (call $validate_string_ret (i32.const 20000)) end ) + "#, + "", + "", + ) + }; + + let component = format!( + r#" +(component + (type $many_params (tuple + string string string string + string string string string + string)) + (import "f1" (func $f1 (param "a" u32) (result u32))) + (import "f2" (func $f2 (param "a" $many_params) (result u32))) + (import "f3" (func $f3 (param "a" u32) (result string))) + (import "f4" (func $f4 (param "a" $many_params) (result string))) + + (core module $libc + {REALLOC_AND_FREE} + (memory (export "memory") 1) + ) + (core instance $libc (instantiate (module $libc))) + + (core func $f1_lower (canon lower (func $f1) + (memory $libc "memory") + (realloc (func $libc "realloc")) + {async_lower_opts} + )) + (core func $f2_lower (canon lower (func $f2) + (memory $libc "memory") + (realloc (func $libc "realloc")) + {async_lower_opts} + )) + (core func $f3_lower (canon lower (func $f3) + (memory $libc "memory") + (realloc (func $libc "realloc")) + {async_lower_opts} + )) + (core func $f4_lower (canon lower (func $f4) + (memory $libc "memory") + (realloc (func $libc "realloc")) + {async_lower_opts} + )) + + (core module $m + (import "libc" "memory" (memory 1)) + (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32))) + (import "host" "task.return" (func $task-return)) + {body} + + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) (func $allocate_empty_strings (result i32) (local $ret i32) @@ -601,6 +703,8 @@ fn stack_and_heap_args_and_rets() -> Result<()> { (data (i32.const 1000) "abc") ) + (core type $task-return-type (func)) + (core func $task-return (canon task.return $task-return-type)) (core instance $m (instantiate $m (with "libc" (instance $libc)) (with "host" (instance @@ -608,130 +712,239 @@ fn stack_and_heap_args_and_rets() -> Result<()> { (export "f2" (func $f2_lower)) (export "f3" (func $f3_lower)) (export "f4" (func $f4_lower)) + (export "task.return" (func $task-return)) )) )) (func (export "run") - (canon lift (core func $m "run")) + (canon lift (core func $m "run") {async_lift_opts}) ) ) "# ); - let engine = super::engine(); + let mut config = Config::new(); + config.wasm_component_model_async(true); + config.async_support(true); + let engine = &Engine::new(&config)?; let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); // First, test the static API let mut linker = Linker::new(&engine); - linker - .root() - .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> { - assert_eq!(x, 1); - Ok((2,)) - })?; - linker.root().func_wrap( - "f2", - |cx: StoreContextMut<'_, ()>, - (arg,): (( - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - ),)| - -> Result<(u32,)> { - assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok((3,)) - }, - )?; - linker - .root() - .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> { - assert_eq!(arg, 8); - Ok(("xyz".to_string(),)) - })?; - linker.root().func_wrap( - "f4", - |cx: StoreContextMut<'_, ()>, - (arg,): (( - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - WasmStr, - ),)| - -> Result<(String,)> { - assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok(("xyz".to_string(),)) - }, - )?; - let instance = linker.instantiate(&mut store, &component)?; - instance - .get_typed_func::<(), ()>(&mut store, "run")? - .call(&mut store, ())?; + if concurrent { + linker + .root() + .func_wrap_concurrent("f1", |_, (x,): (u32,)| { + assert_eq!(x, 1); + async { component::for_any(|_| Ok((2u32,))) } + })?; + linker.root().func_wrap_concurrent( + "f2", + |cx: StoreContextMut<'_, ()>, + (arg,): (( + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + ),)| { + assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); + async { component::for_any(|_| Ok((3u32,))) } + }, + )?; + linker + .root() + .func_wrap_concurrent("f3", |_, (arg,): (u32,)| { + assert_eq!(arg, 8); + async { component::for_any(|_| Ok(("xyz".to_string(),))) } + })?; + linker.root().func_wrap_concurrent( + "f4", + |cx: StoreContextMut<'_, ()>, + (arg,): (( + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + ),)| { + assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); + async { component::for_any(|_| Ok(("xyz".to_string(),))) } + }, + )?; + } else { + linker + .root() + .func_wrap("f1", |_, (x,): (u32,)| -> Result<(u32,)> { + assert_eq!(x, 1); + Ok((2,)) + })?; + linker.root().func_wrap( + "f2", + |cx: StoreContextMut<'_, ()>, + (arg,): (( + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + ),)| + -> Result<(u32,)> { + assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); + Ok((3,)) + }, + )?; + linker + .root() + .func_wrap("f3", |_, (arg,): (u32,)| -> Result<(String,)> { + assert_eq!(arg, 8); + Ok(("xyz".to_string(),)) + })?; + linker.root().func_wrap( + "f4", + |cx: StoreContextMut<'_, ()>, + (arg,): (( + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + WasmStr, + ),)| + -> Result<(String,)> { + assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); + Ok(("xyz".to_string(),)) + }, + )?; + } + + let instance = linker.instantiate_async(&mut store, &component).await?; + let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; + + if concurrent { + let promise = run.call_concurrent(&mut store, ()).await?; + promise.get(&mut store).await?; + } else { + run.call_async(&mut store, ()).await?; + } // Next, test the dynamic API let mut linker = Linker::new(&engine); - linker.root().func_new("f1", |_, args, results| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 1); - results[0] = Val::U32(2); - Ok(()) - } else { - panic!() - } - })?; - linker.root().func_new("f2", |_, args, results| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple[0] { - assert_eq!(s.deref(), "abc"); - results[0] = Val::U32(3); + if concurrent { + linker.root().func_new_concurrent("f1", |_, args| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 1); + async { component::for_any(|_| Ok(vec![Val::U32(2)])) } + } else { + panic!() + } + })?; + linker.root().func_new_concurrent("f2", |_, args| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple[0] { + assert_eq!(s.deref(), "abc"); + async { component::for_any(|_| Ok(vec![Val::U32(3)])) } + } else { + panic!() + } + } else { + panic!() + } + })?; + linker.root().func_new_concurrent("f3", |_, args| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 8); + async { component::for_any(|_| Ok(vec![Val::String("xyz".into())])) } + } else { + panic!(); + } + })?; + linker.root().func_new_concurrent("f4", |_, args| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple[0] { + assert_eq!(s.deref(), "abc"); + async { component::for_any(|_| Ok(vec![Val::String("xyz".into())])) } + } else { + panic!() + } + } else { + panic!() + } + })?; + } else { + linker.root().func_new("f1", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 1); + results[0] = Val::U32(2); Ok(()) } else { panic!() } - } else { - panic!() - } - })?; - linker.root().func_new("f3", |_, args, results| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 8); - results[0] = Val::String("xyz".into()); - Ok(()) - } else { - panic!(); - } - })?; - linker.root().func_new("f4", |_, args, results| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple[0] { - assert_eq!(s.deref(), "abc"); + })?; + linker.root().func_new("f2", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::U32(3); + Ok(()) + } else { + panic!() + } + } else { + panic!() + } + })?; + linker.root().func_new("f3", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 8); results[0] = Val::String("xyz".into()); Ok(()) + } else { + panic!(); + } + })?; + linker.root().func_new("f4", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::String("xyz".into()); + Ok(()) + } else { + panic!() + } } else { panic!() } - } else { - panic!() - } - })?; - let instance = linker.instantiate(&mut store, &component)?; - instance - .get_func(&mut store, "run") - .unwrap() - .call(&mut store, &[], &mut [])?; + })?; + } + + let instance = linker.instantiate_async(&mut store, &component).await?; + let run = instance.get_func(&mut store, "run").unwrap(); + + if concurrent { + let promise = run.call_concurrent(&mut store, Vec::new()).await?; + promise.get(&mut store).await?; + } else { + run.call_async(&mut store, &[], &mut []).await?; + } Ok(()) } 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" ), } diff --git a/tests/misc_testsuite/component-model-async/error-context.wast b/tests/misc_testsuite/component-model-async/error-context.wast new file mode 100644 index 000000000000..e564416a5109 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/error-context.wast @@ -0,0 +1,35 @@ +;;! component_model_async = true + +;; error-context.new +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "error-context.new" (func $error-context-new (param i32 i32) (result i32))) + ) + (core func $error-context-new (canon error-context.new (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "error-context.new" (func $error-context-new)))))) +) + +;; error-context.debug-message +(component + (core module $libc + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + (memory (export "memory") 1) + ) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "error-context.debug-message" (func $error-context-debug-message (param i32 i32))) + ) + (core func $error-context-debug-message (canon error-context.debug-message (memory $libc "memory") (realloc (func $libc "realloc")))) + (core instance $i (instantiate $m (with "" (instance (export "error-context.debug-message" (func $error-context-debug-message)))))) +) + +;; error-context.drop +(component + (core module $m + (import "" "error-context.drop" (func $error-context-drop (param i32))) + ) + (core func $error-context-drop (canon error-context.drop)) + (core instance $i (instantiate $m (with "" (instance (export "error-context.drop" (func $error-context-drop)))))) +) diff --git a/tests/misc_testsuite/component-model-async/fused.wast b/tests/misc_testsuite/component-model-async/fused.wast new file mode 100644 index 000000000000..f9b39258c9df --- /dev/null +++ b/tests/misc_testsuite/component-model-async/fused.wast @@ -0,0 +1,55 @@ +;;! component_model_async = true +;;! reference_types = true +;;! gc_types = true +;;! multi_memory = true + +;; async lower -> async lift +(component + (component $lifter + (core module $m + (import "" "task.return" (func $task-return (param i32))) + (func (export "foo") (param i32) (call $task-return (local.get 0))) + ) + (core type $task-return (func (param i32))) + (core func $task-return (canon task.return $task-return)) + (core instance $i (instantiate $m + (with "" (instance (export "task.return" (func $task-return)))) + )) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async) + ) + ) + + (component $lowerer + (import "a" (func $foo (param "p1" u32) (result u32))) + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core func $foo (canon lower (func $foo) async (memory $libc "memory"))) + (core module $m + (import "libc" "memory" (memory 1)) + (import "" "foo" (func $foo (param i32 i32) (result i32))) + (func (export "run") + block + (i32.store offset=0 (i32.const 1200) (i32.const 42)) + (call $foo (i32.const 1200) (i32.const 1204)) + (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) + br_if 0 + unreachable + end + ) + ) + (core instance $i (instantiate $m + (with "libc" (instance $libc)) + (with "" (instance (export "foo" (func $foo)))) + )) + (func (export "run") (canon lift (core func $i "run"))) + ) + + (instance $lifter (instantiate $lifter)) + (instance $lowerer (instantiate $lowerer (with "a" (func $lifter "foo")))) + (func (export "run") (alias export $lowerer "run")) +) + +;; TODO: this requires async support in `wasmtime-wast`: +;;(assert_return (invoke "run")) diff --git a/tests/misc_testsuite/component-model-async/futures.wast b/tests/misc_testsuite/component-model-async/futures.wast new file mode 100644 index 000000000000..f1e4d4d5b940 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/futures.wast @@ -0,0 +1,90 @@ +;;! component_model_async = true + +;; future.new +(component + (core module $m + (import "" "future.new" (func $future-new (result i32))) + ) + (type $future-type (future u8)) + (core func $future-new (canon future.new $future-type)) + (core instance $i (instantiate $m (with "" (instance (export "future.new" (func $future-new)))))) +) + +;; future.read +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "future.read" (func $future-read (param i32 i32) (result i32))) + ) + (type $future-type (future u8)) + (core func $future-read (canon future.read $future-type async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "future.read" (func $future-read)))))) +) + +;; future.read; with realloc +(component + (core module $libc + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + (memory (export "memory") 1) + ) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "future.read" (func $future-read (param i32 i32) (result i32))) + ) + (type $future-type (future string)) + (core func $future-read (canon future.read $future-type async (memory $libc "memory") (realloc (func $libc "realloc")))) + (core instance $i (instantiate $m (with "" (instance (export "future.read" (func $future-read)))))) +) + +;; future.write +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "future.write" (func $future-write (param i32 i32) (result i32))) + ) + (type $future-type (future u8)) + (core func $future-write (canon future.write $future-type async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "future.write" (func $future-write)))))) +) + +;; future.cancel-read +(component + (core module $m + (import "" "future.cancel-read" (func $future-cancel-read (param i32) (result i32))) + ) + (type $future-type (future u8)) + (core func $future-cancel-read (canon future.cancel-read $future-type async)) + (core instance $i (instantiate $m (with "" (instance (export "future.cancel-read" (func $future-cancel-read)))))) +) + +;; future.cancel-write +(component + (core module $m + (import "" "future.cancel-write" (func $future-cancel-write (param i32) (result i32))) + ) + (type $future-type (future u8)) + (core func $future-cancel-write (canon future.cancel-write $future-type async)) + (core instance $i (instantiate $m (with "" (instance (export "future.cancel-write" (func $future-cancel-write)))))) +) + +;; future.close-readable +(component + (core module $m + (import "" "future.close-readable" (func $future-close-readable (param i32))) + ) + (type $future-type (future u8)) + (core func $future-close-readable (canon future.close-readable $future-type)) + (core instance $i (instantiate $m (with "" (instance (export "future.close-readable" (func $future-close-readable)))))) +) + +;; future.close-writable +(component + (core module $m + (import "" "future.close-writable" (func $future-close-writable (param i32 i32))) + ) + (type $future-type (future u8)) + (core func $future-close-writable (canon future.close-writable $future-type)) + (core instance $i (instantiate $m (with "" (instance (export "future.close-writable" (func $future-close-writable)))))) +) diff --git a/tests/misc_testsuite/component-model-async/lift.wast b/tests/misc_testsuite/component-model-async/lift.wast new file mode 100644 index 000000000000..f90c65672c96 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/lift.wast @@ -0,0 +1,26 @@ +;;! component_model_async = true + +;; async lift; no callback +(component + (core module $m + (func (export "foo") (param i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async) + ) +) + +;; async lift; with callback +(component + (core module $m + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + (func (export "foo") (param i32) (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async (callback (func $i "callback"))) + ) +) diff --git a/tests/misc_testsuite/component-model-async/lower.wast b/tests/misc_testsuite/component-model-async/lower.wast new file mode 100644 index 000000000000..bcb4862fc8b4 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/lower.wast @@ -0,0 +1,13 @@ +;;! component_model_async = true + +;; async lower +(component + (import "host-echo-u32" (func $foo (param "p1" u32) (result u32))) + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core func $foo (canon lower (func $foo) async (memory $libc "memory"))) + (core module $m + (func (import "" "foo") (param i32 i32) (result i32)) + ) + (core instance $i (instantiate $m (with "" (instance (export "foo" (func $foo)))))) +) diff --git a/tests/misc_testsuite/component-model-async/streams.wast b/tests/misc_testsuite/component-model-async/streams.wast new file mode 100644 index 000000000000..790ddec7e5f8 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/streams.wast @@ -0,0 +1,90 @@ +;;! component_model_async = true + +;; stream.new +(component + (core module $m + (import "" "stream.new" (func $stream-new (result i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-new (canon stream.new $stream-type)) + (core instance $i (instantiate $m (with "" (instance (export "stream.new" (func $stream-new)))))) +) + +;; stream.read +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "stream.read" (func $stream-read (param i32 i32 i32) (result i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-read (canon stream.read $stream-type async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "stream.read" (func $stream-read)))))) +) + +;; stream.read; with realloc +(component + (core module $libc + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + (memory (export "memory") 1) + ) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "stream.read" (func $stream-read (param i32 i32 i32) (result i32))) + ) + (type $stream-type (stream string)) + (core func $stream-read (canon stream.read $stream-type async (memory $libc "memory") (realloc (func $libc "realloc")))) + (core instance $i (instantiate $m (with "" (instance (export "stream.read" (func $stream-read)))))) +) + +;; stream.write +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "stream.write" (func $stream-write (param i32 i32 i32) (result i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-write (canon stream.write $stream-type async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "stream.write" (func $stream-write)))))) +) + +;; stream.cancel-read +(component + (core module $m + (import "" "stream.cancel-read" (func $stream-cancel-read (param i32) (result i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-cancel-read (canon stream.cancel-read $stream-type async)) + (core instance $i (instantiate $m (with "" (instance (export "stream.cancel-read" (func $stream-cancel-read)))))) +) + +;; stream.cancel-write +(component + (core module $m + (import "" "stream.cancel-write" (func $stream-cancel-write (param i32) (result i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-cancel-write (canon stream.cancel-write $stream-type async)) + (core instance $i (instantiate $m (with "" (instance (export "stream.cancel-write" (func $stream-cancel-write)))))) +) + +;; stream.close-readable +(component + (core module $m + (import "" "stream.close-readable" (func $stream-close-readable (param i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-close-readable (canon stream.close-readable $stream-type)) + (core instance $i (instantiate $m (with "" (instance (export "stream.close-readable" (func $stream-close-readable)))))) +) + +;; stream.close-writable +(component + (core module $m + (import "" "stream.close-writable" (func $stream-close-writable (param i32 i32))) + ) + (type $stream-type (stream u8)) + (core func $stream-close-writable (canon stream.close-writable $stream-type)) + (core instance $i (instantiate $m (with "" (instance (export "stream.close-writable" (func $stream-close-writable)))))) +) diff --git a/tests/misc_testsuite/component-model-async/task-builtins.wast b/tests/misc_testsuite/component-model-async/task-builtins.wast new file mode 100644 index 000000000000..a5f9ca0f468e --- /dev/null +++ b/tests/misc_testsuite/component-model-async/task-builtins.wast @@ -0,0 +1,60 @@ +;;! component_model_async = true + +;; task.backpressure +(component + (core module $m + (import "" "task.backpressure" (func $task-backpressure (param i32))) + ) + (core func $task-backpressure (canon task.backpressure)) + (core instance $i (instantiate $m (with "" (instance (export "task.backpressure" (func $task-backpressure)))))) +) + +;; task.return +(component + (core module $m + (import "" "task.return" (func $task-return (param i32))) + ) + (core type $task-return-type (func (param i32))) + (core func $task-return (canon task.return $task-return-type)) + (core instance $i (instantiate $m (with "" (instance (export "task.return" (func $task-return)))))) +) + +;; task.wait +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "task.wait" (func $task-wait (param i32) (result i32))) + ) + (core func $task-wait (canon task.wait async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "task.wait" (func $task-wait)))))) +) + +;; task.poll +(component + (core module $libc (memory (export "memory") 1)) + (core instance $libc (instantiate $libc)) + (core module $m + (import "" "task.poll" (func $task-poll (param i32) (result i32))) + ) + (core func $task-poll (canon task.poll async (memory $libc "memory"))) + (core instance $i (instantiate $m (with "" (instance (export "task.poll" (func $task-poll)))))) +) + +;; task.yield +(component + (core module $m + (import "" "task.yield" (func $task-yield)) + ) + (core func $task-yield (canon task.yield async)) + (core instance $i (instantiate $m (with "" (instance (export "task.yield" (func $task-yield)))))) +) + +;; subtask.drop +(component + (core module $m + (import "" "subtask.drop" (func $subtask-drop (param i32))) + ) + (core func $subtask-drop (canon subtask.drop)) + (core instance $i (instantiate $m (with "" (instance (export "subtask.drop" (func $subtask-drop)))))) +)