From 9ad437eecc03b9e8e533f00edac48b2f19e68e7f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 21 May 2025 12:35:42 -0700 Subject: [PATCH 01/12] Update for new async ABI changes This commit updates to account for WebAssembly/component-model#520. This is a large-ish change to the semantics from a runtime perspective and needed a number of changes: * The `sync_prepare_call` and `async_prepare_call` libcalls were merged together. The previous async version statically took two arguments but now it's taking a variable number of arguments which looked quite a lot like `sync_prepare_call` so they're now merged into one. * Lots of little updates were made to `fact/signatures.rs` to account for ABI changes. * Tests with handwritten signatures were all updated to the new ABI. * The `CallerInfo::Async` structure which "buffers" a call was updated to have a `Vec` for the incoming parameters. This is a particularly inefficient way to store parameters but it's in theory workable for now. * The `Storage` abstraction in host calls was refactored and updated to account for async and how lifting parameters could be either flat or indirect. Similar updates were made to the dynamic path as well --- Cargo.lock | 51 +- Cargo.toml | 30 +- crates/cranelift/src/compiler/component.rs | 41 +- crates/environ/src/component.rs | 31 +- crates/environ/src/component/dfg.rs | 10 +- crates/environ/src/component/info.rs | 16 +- crates/environ/src/component/translate.rs | 1 + .../environ/src/component/translate/adapt.rs | 13 +- crates/environ/src/fact.rs | 69 +-- crates/environ/src/fact/signature.rs | 157 +++--- crates/environ/src/fact/trampoline.rs | 187 ++++--- .../src/bin/async_cancel_callee.rs | 20 +- .../src/bin/async_cancel_caller.rs | 43 +- .../src/bin/async_poll_stackless.rs | 4 +- .../src/bin/async_poll_synchronous.rs | 4 +- .../src/bin/async_round_trip_stackful.rs | 11 +- .../src/runtime/component/concurrent.rs | 112 ++-- .../src/runtime/component/func/host.rs | 488 +++++++++++++----- .../src/runtime/vm/component/libcalls.rs | 37 +- src/commands/objdump.rs | 5 +- tests/all/component_model/bindgen.rs | 4 +- tests/all/component_model/import.rs | 8 +- .../backpressure-deadlock.wast | 4 +- .../component-model-async/drop-subtask.wast | 4 +- .../drop-waitable-set.wast | 6 +- .../component-model-async/empty-wait.wast | 8 +- .../component-model-async/fused.wast | 9 +- .../component-model-async/wait-forever2.wast | 4 +- 28 files changed, 723 insertions(+), 654 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 916453ec54..c3d497d35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4079,8 +4079,7 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3efe9b91ee8d09c6696e448d756a207c290deb6af2e92df6acfd8bbde3bb7f70" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "heck 0.4.1", @@ -4100,8 +4099,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "leb128fmt", "wasmparser 0.230.0", @@ -4110,8 +4108,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a52e010df5494f4289ccc68ce0c2a8c17555225a5e55cc41b98f5ea28d0844b" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "indexmap 2.7.0", @@ -4122,8 +4119,7 @@ dependencies = [ [[package]] name = "wasm-mutate" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c9228682f308eb59db56835e1113ac2951d92ad92cd7bdcd9ab3cee607ed4f" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "egg", "log", @@ -4136,8 +4132,7 @@ dependencies = [ [[package]] name = "wasm-smith" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac6b9a65a2552ce3279294e877cc4b408d6d2b771445feb215f24aff3093d6f" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "arbitrary", @@ -4159,8 +4154,7 @@ dependencies = [ [[package]] name = "wasm-wave" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66809a0fb9e6555af54d3a388296b0859263d7ff520e4dad94218a301e9f44bd" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "indexmap 2.7.0", "logos", @@ -4225,8 +4219,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -4238,8 +4231,7 @@ dependencies = [ [[package]] name = "wasmprinter" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dc8e9a1e48f4b2247b006b3a9b0a02ba62a2e52cfcfd4bc4c70785a6104fc32" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "termcolor", @@ -4919,8 +4911,7 @@ dependencies = [ [[package]] name = "wast" version = "230.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "bumpalo", "leb128fmt", @@ -4932,8 +4923,7 @@ dependencies = [ [[package]] name = "wat" version = "1.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "wast 230.0.0", ] @@ -5223,8 +5213,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa5b79cd8cb4b27a9be3619090c03cbb87fe7b1c6de254b4c9b4477188828af8" +source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" dependencies = [ "wit-bindgen-rt 0.42.1", "wit-bindgen-rust-macro", @@ -5233,8 +5222,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35e550f614e16db196e051d22b0d4c94dd6f52c90cb1016240f71b9db332631" +source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" dependencies = [ "anyhow", "heck 0.5.0", @@ -5262,8 +5250,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051105bab12bc78e161f8dfb3596e772dd6a01ebf9c4840988e00347e744966a" +source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" dependencies = [ "bitflags 2.6.0", "futures", @@ -5273,8 +5260,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb1e0a91fc85f4ef70e0b81cd86c2b49539d3cd14766fd82396184aadf8cb7d7" +source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" dependencies = [ "anyhow", "heck 0.5.0", @@ -5289,8 +5275,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce69f52c5737705881d5da5a1dd06f47f8098d094a8d65a3e44292942edb571f" +source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" dependencies = [ "anyhow", "prettyplease", @@ -5304,8 +5289,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607b15ead6d0e87f5d1613b4f18c04d4e80ceeada5ffa608d8360e6909881df" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -5323,8 +5307,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "679fde5556495f98079a8e6b9ef8c887f731addaffa3d48194075c1dd5cd611b" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 44a599cce4..bf3f77d666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -589,21 +589,21 @@ lto = true # TODO: remove this once we've switched to a wasm-tools/wit-bindgen release: [patch.crates-io] -# wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wat = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wast = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasmprinter = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-smith = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-mutate = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-wave = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wit-bindgen = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } -# wit-bindgen-rt = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } -# wit-bindgen-rust-macro = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } +wasmparser = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wat = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wast = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasmprinter = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-encoder = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-smith = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-mutate = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wit-parser = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wit-component = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-wave = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-compose = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasm-metadata = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wit-bindgen = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } +wit-bindgen-rt = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } +wit-bindgen-rust-macro = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } # wasmparser = { path = '../wasm-tools/crates/wasmparser' } # wat = { path = '../wasm-tools/crates/wat' } diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index f394a1b97c..6f9a01d370 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -6,7 +6,7 @@ use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value}; use cranelift_codegen::isa::{CallConv, TargetIsa}; use cranelift_frontend::FunctionBuilder; -use wasmtime_environ::fact::SYNC_PREPARE_FIXED_PARAMS; +use wasmtime_environ::fact::PREPARE_CALL_FIXED_PARAMS; use wasmtime_environ::{CompiledFunctionBody, component::*}; use wasmtime_environ::{ HostCall, ModuleInternedTypeIndex, PtrSize, TrapSentinel, Tunables, WasmFuncType, WasmValType, @@ -255,9 +255,8 @@ impl<'a> TrampolineCompiler<'a> { me.raise_if_host_trapped(rets.pop().unwrap()); }) } - Trampoline::SyncPrepareCall { memory } => self.translate_sync_prepare(*memory), + Trampoline::PrepareCall { memory } => self.translate_prepare(*memory), Trampoline::SyncStartCall { callback } => self.translate_sync_start(*callback), - Trampoline::AsyncPrepareCall { memory } => self.translate_async_prepare(*memory), Trampoline::AsyncStartCall { callback, post_return, @@ -471,7 +470,7 @@ impl<'a> TrampolineCompiler<'a> { ); } - fn translate_sync_prepare(&mut self, memory: Option) { + fn translate_prepare(&mut self, memory: Option) { match self.abi { Abi::Wasm => {} @@ -489,8 +488,8 @@ impl<'a> TrampolineCompiler<'a> { let pointer_type = self.isa.pointer_type(); let wasm_func_ty = &self.types[self.signature].unwrap_func(); - let param_offset = SYNC_PREPARE_FIXED_PARAMS.len(); - let spill_offset = param_offset + 2; + let param_offset = PREPARE_CALL_FIXED_PARAMS.len(); + let spill_offset = param_offset + 2; // skip caller/callee vmctx let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( &WasmFuncType::new( @@ -517,7 +516,7 @@ impl<'a> TrampolineCompiler<'a> { self.translate_intrinsic_libcall( vmctx, - host::sync_prepare, + host::prepare_call, &callee_args, TrapSentinel::Falsy, ); @@ -572,34 +571,6 @@ impl<'a> TrampolineCompiler<'a> { self.builder.ins().return_(&results); } - fn translate_async_prepare(&mut self, memory: Option) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // This code can only be called from (FACT-generated) Wasm, so - // we don't need to support the array ABI. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![vmctx, self.load_optional_memory(vmctx, memory)]; - - // remaining parameters - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::async_prepare, - &callee_args, - TrapSentinel::Falsy, - ); - } - fn translate_async_start( &mut self, callback: Option, diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index 1f607d78ff..a4036b5b57 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -32,11 +32,26 @@ /// for transferring parameters. pub const MAX_FLAT_PARAMS: usize = 16; +/// Similar to `MAX_FLAT_PARAMS`, but used for async-lowered imports instead of +/// sync ones. +pub const MAX_FLAT_ASYNC_PARAMS: usize = 4; + /// Canonical ABI-defined constant for the maximum number of "flat" results. /// This number of results are returned directly from wasm and otherwise results /// are transferred through memory. pub const MAX_FLAT_RESULTS: usize = 1; +/// Sentinel value in `result_count_or_max_if_async` as part of the +/// `prepare_call` libcall which indicates that preparation is being done for an +/// async function that produces no result, aka there is no return pointer. +pub const PREPARE_ASYNC_NO_RESULT: u32 = u32::MAX; + +/// Sentinel value in `result_count_or_max_if_async` as part of the +/// `prepare_call` libcall which indicates that preparation is being done for an +/// async function that produces at least one result, aka there is a return +/// pointer. +pub const PREPARE_ASYNC_WITH_RESULT: u32 = u32::MAX - 1; + mod artifacts; mod info; mod names; @@ -106,12 +121,22 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] subtask_cancel(vmctx: vmctx, caller_instance: u32, async_: u8, task_id: u32) -> u64; #[cfg(feature = "component-model-async")] - sync_prepare(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, result_count: u32, storage: ptr_u8, storage_len: size) -> bool; + prepare_call( + vmctx: vmctx, + memory: ptr_u8, + start: ptr_u8, + return_: ptr_u8, + caller_instance: u32, + callee_instance: u32, + task_return_type: u32, + string_encoding: u32, + result_count_or_max_if_async: u32, + storage: ptr_u8, + torage_len: size + ) -> bool; #[cfg(feature = "component-model-async")] sync_start(vmctx: vmctx, callback: ptr_u8, callee: ptr_u8, param_count: u32, storage: ptr_u8, storage_len: size) -> bool; #[cfg(feature = "component-model-async")] - async_prepare(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, params: u32, results: u32) -> bool; - #[cfg(feature = "component-model-async")] async_start(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, callee: ptr_u8, param_count: u32, result_count: u32, flags: u32) -> u64; #[cfg(feature = "component-model-async")] future_new(vmctx: vmctx, ty: u32) -> u64; diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index 22b0f3af52..5f6f7c7d29 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -383,15 +383,12 @@ pub enum Trampoline { ResourceTransferBorrow, ResourceEnterCall, ResourceExitCall, - SyncPrepareCall { + PrepareCall { memory: Option, }, SyncStartCall { callback: Option, }, - AsyncPrepareCall { - memory: Option, - }, AsyncStartCall { callback: Option, post_return: Option, @@ -895,15 +892,12 @@ impl LinearizeDfg<'_> { Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow, Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall, Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall, - Trampoline::SyncPrepareCall { memory } => info::Trampoline::SyncPrepareCall { + Trampoline::PrepareCall { memory } => info::Trampoline::PrepareCall { memory: memory.map(|v| self.runtime_memory(v)), }, Trampoline::SyncStartCall { callback } => info::Trampoline::SyncStartCall { callback: callback.map(|v| self.runtime_callback(v)), }, - Trampoline::AsyncPrepareCall { memory } => info::Trampoline::AsyncPrepareCall { - memory: memory.map(|v| self.runtime_memory(v)), - }, Trampoline::AsyncStartCall { callback, post_return, diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index b65a01f1df..acbd0f93dd 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -957,8 +957,8 @@ pub enum Trampoline { ResourceExitCall, /// An intrinsic used by FACT-generated modules to prepare a call involving - /// a sync-lowered import and async-lifted export. - SyncPrepareCall { + /// a sync/async-lowered import and sync/async-lifted export. + PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime matches the one specified in /// the lifted export. @@ -972,15 +972,6 @@ pub enum Trampoline { callback: Option, }, - /// An intrinsic used by FACT-generated modules to prepare a call involving - /// an async-lowered import function. - AsyncPrepareCall { - /// The memory used to verify that the memory specified for the - /// `task.return` that is called at runtime (if any) matches the one - /// specified in the lifted export. - memory: Option, - }, - /// An intrinsic used by FACT-generated modules to start a call involving /// an async-lowered import function. /// @@ -1086,9 +1077,8 @@ impl Trampoline { ResourceTransferBorrow => format!("component-resource-transfer-borrow"), ResourceEnterCall => format!("component-resource-enter-call"), ResourceExitCall => format!("component-resource-exit-call"), - SyncPrepareCall { .. } => format!("component-sync-prepare-call"), + PrepareCall { .. } => format!("component-prepare-call"), SyncStartCall { .. } => format!("component-sync-start-call"), - AsyncPrepareCall { .. } => format!("component-async-prepare-call"), AsyncStartCall { .. } => format!("component-async-start-call"), FutureTransfer => format!("future-transfer"), StreamTransfer => format!("stream-transfer"), diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 60c333f12e..24ec3265ac 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -1251,6 +1251,7 @@ impl<'a, 'data> Translator<'a, 'data> { ret.callback = Some(idx); } wasmparser::CanonicalOption::CoreType(_) => todo!(), + wasmparser::CanonicalOption::Gc => todo!(), } } return ret; diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 94ff0a67e3..277afd8d08 100644 --- a/crates/environ/src/component/translate/adapt.rs +++ b/crates/environ/src/component/translate/adapt.rs @@ -305,21 +305,14 @@ fn fact_import_to_core_def( } fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall), fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall), - fact::Import::SyncPrepareCall { memory } => { - simple_intrinsic(dfg::Trampoline::SyncPrepareCall { - memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), - }) - } + fact::Import::PrepareCall { memory } => simple_intrinsic(dfg::Trampoline::PrepareCall { + memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), + }), fact::Import::SyncStartCall { callback } => { simple_intrinsic(dfg::Trampoline::SyncStartCall { callback: callback.clone().map(|v| dfg.callbacks.push(v)), }) } - fact::Import::AsyncPrepareCall { memory } => { - simple_intrinsic(dfg::Trampoline::AsyncPrepareCall { - memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), - }) - } fact::Import::AsyncStartCall { callback, post_return, diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index a1d48ce610..d6e9aa1f56 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -43,19 +43,19 @@ mod traps; /// callee is an async-lifted export. pub const START_FLAG_ASYNC_CALLEE: i32 = 1 << 0; -/// Fixed parameter types for the `sync-prepare` built-in function. +/// Fixed parameter types for the `prepare_call` built-in function. /// -/// Note that `sync-prepare` also takes a variable number of parameters in +/// Note that `prepare_call` also takes a variable number of parameters in /// addition to these, determined by the signature of the function for which /// we're generating an adapter. -pub static SYNC_PREPARE_FIXED_PARAMS: &[ValType] = &[ - ValType::FUNCREF, - ValType::FUNCREF, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, +pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[ + ValType::FUNCREF, // start + ValType::FUNCREF, // return + ValType::I32, // caller_instance + ValType::I32, // callee_instance + ValType::I32, // task_return_type + ValType::I32, // string_encoding + ValType::I32, // result_count_or_max_if_async ]; /// Representation of an adapter module. @@ -88,7 +88,6 @@ pub struct Module<'a> { imported_resource_exit_call: Option, // Cached versions of imported trampolines for working with the async ABI. - imported_async_prepare_calls: HashMap, FuncIndex>, imported_async_start_calls: HashMap<(Option, Option), FuncIndex>, // Cached versions of imported trampolines for working with `stream`s, @@ -224,7 +223,6 @@ impl<'a> Module<'a> { imported_resource_transfer_borrow: None, imported_resource_enter_call: None, imported_resource_exit_call: None, - imported_async_prepare_calls: HashMap::new(), imported_async_start_calls: HashMap::new(), imported_future_transfer: None, imported_stream_transfer: None, @@ -485,14 +483,14 @@ impl<'a> Module<'a> { /// the parameters, the adapter must use this function to set up the subtask /// and stash the parameters as part of that subtask until any backpressure /// has cleared. - fn import_sync_prepare_call( + fn import_prepare_call( &mut self, suffix: &str, params: &[ValType], memory: Option, ) -> FuncIndex { let ty = self.core_types.function( - &SYNC_PREPARE_FIXED_PARAMS + &PREPARE_CALL_FIXED_PARAMS .iter() .copied() .chain(params.iter().copied()) @@ -504,7 +502,7 @@ impl<'a> Module<'a> { &format!("[prepare-call]{suffix}"), EntityType::Function(ty), ); - let import = Import::SyncPrepareCall { + let import = Import::PrepareCall { memory: memory.map(|v| self.imported_memories[v].clone()), }; self.imports.push(import); @@ -544,35 +542,6 @@ impl<'a> Module<'a> { self.imported_funcs.push(None) } - /// Import a host built-in function to set up a subtask for an async-lowered - /// import call to an async- or sync-lifted export. - fn import_async_prepare_call( - &mut self, - suffix: &str, - memory: Option, - ) -> FuncIndex { - self.import_simple_get_and_set( - "async", - &format!("[prepare-call]{suffix}"), - &[ - ValType::FUNCREF, - ValType::FUNCREF, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ], - &[], - Import::AsyncPrepareCall { - memory: memory.map(|v| self.imported_memories[v].clone()), - }, - |me| me.imported_async_prepare_calls.get(&memory).copied(), - |me, v| assert!(me.imported_async_prepare_calls.insert(memory, v).is_none()), - ) - } - /// Import a host built-in function to start a subtask for an async-lowered /// import call to an async- or sync-lifted export. /// @@ -822,8 +791,8 @@ pub enum Import { /// metadata. ResourceExitCall, /// An intrinsic used by FACT-generated modules to begin a call involving a - /// sync-lowered import and async-lifted export. - SyncPrepareCall { + /// sync/async-lowered import and sync/async-lifted export. + PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime (if any) matches the one /// specified in the lifted export. @@ -835,14 +804,6 @@ pub enum Import { /// The callee's callback function, if any. callback: Option, }, - /// An intrinsic used by FACT-generated modules to begin a call involving an - /// async-lowered import function. - AsyncPrepareCall { - /// The memory used to verify that the memory specified for the - /// `task.return` that is called at runtime (if any) matches the one - /// specified in the lifted export. - memory: Option, - }, /// An intrinsic used by FACT-generated modules to complete a call involving /// an async-lowered import function. AsyncStartCall { diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index c62e254045..5d93b0007b 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -1,6 +1,8 @@ //! Size, align, and flattening information about component model types. -use crate::component::{ComponentTypesBuilder, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS}; +use crate::component::{ + ComponentTypesBuilder, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, +}; use crate::fact::{AdapterOptions, Context, Options}; use crate::prelude::*; use wasm_encoder::ValType; @@ -26,77 +28,78 @@ impl ComponentTypesBuilder { let ty = &self[options.ty]; let ptr_ty = options.options.ptr(); - // The async lower ABI is always `(param i32 i32) (result i32)` (for - // wasm32, anyway), regardless of the component-level signature. - // - // The first param is a pointer to linear memory where the parameters have - // been stored by the caller, the second param is a pointer to linear - // memory where the results should be stored by the callee, and the - // result is a status code optionally ORed with a subtask ID. - if let (Context::Lower, true) = (&context, options.options.async_) { - return Signature { - params: vec![ptr_ty; 2], - results: vec![ValType::I32], - }; - } + let max_flat_params = match (&context, options.options.async_) { + // Async imports have a lower limit on their flat parameters than + // normal functions, so account for that here. + (Context::Lower, true) => MAX_FLAT_ASYNC_PARAMS, + _ => MAX_FLAT_PARAMS, + }; // If we're lifting async or sync, or if we're lowering sync, we can - // pass up to `MAX_FLAT_PARAMS` via the stack. + // pass up to `max_flat_params` via the stack. let mut params = match self.flatten_types( &options.options, - MAX_FLAT_PARAMS, + max_flat_params, self[ty.params].types.iter().copied(), ) { Some(list) => list, - None => { - vec![ptr_ty] - } + None => vec![ptr_ty], }; - // If we're lifting async with a callback, the result is an `i32` status - // code, optionally ORed with a guest task identifier, and the result - // will be returned via `task.return`. - // - // If we're lifting async without a callback, then there's no need to return - // anything here since the result will be returned via `task.return` and the - // guest will use `task.wait` rather than return a status code in order to suspend - // itself, if necessary. - if options.options.async_ { - return Signature { - params, - results: if options.options.callback.is_some() { + let results = match (&context, options.options.async_) { + // Async lowered functions store their results in a pointer passed + // as a function argument, so if results are present then add + // another pointer to the list of parameters. + // + // The actual ABI results are then a status code, so account for + // that here. + (Context::Lower, true) => { + if !self[ty.results].types.is_empty() { + params.push(ptr_ty); + } + vec![ValType::I32] + } + + // Async lifted functions transmit results through `task.return` + // meaning there's no handling in the ABI of results. If this is a + // callback-based functions then the export returns a status code, + // otherwise for stackful functions there is no return value. + (Context::Lift, true) => { + if options.options.callback.is_some() { vec![ptr_ty] } else { Vec::new() - }, - }; - } + } + } - // If we've reached this point, we're either lifting or lowering sync, - // in which case the guest will return up to `MAX_FLAT_RESULTS` via the - // stack or spill to linear memory otherwise. - let results = match self.flatten_types( - &options.options, - MAX_FLAT_RESULTS, - self[ty.results].types.iter().copied(), - ) { - Some(list) => list, - None => { - match context { - // For a lifted function too-many-results gets translated to a - // returned pointer where results are read from. The callee - // allocates space here. - Context::Lift => vec![ptr_ty], - // For a lowered function too-many-results becomes a return - // pointer which is passed as the last argument. The caller - // allocates space here. - Context::Lower => { - params.push(ptr_ty); - Vec::new() + // If we've reached this point, we're either lifting or lowering + // sync, in which case the guest will return up to + // `MAX_FLAT_RESULTS` via the stack or spill to linear memory + // otherwise. + (_, false) => match self.flatten_types( + &options.options, + MAX_FLAT_RESULTS, + self[ty.results].types.iter().copied(), + ) { + Some(list) => list, + None => { + match context { + // For a lifted function too-many-results gets translated to a + // returned pointer where results are read from. The callee + // allocates space here. + Context::Lift => vec![ptr_ty], + // For a lowered function too-many-results becomes a return + // pointer which is passed as the last argument. The caller + // allocates space here. + Context::Lower => { + params.push(ptr_ty); + Vec::new() + } } } - } + }, }; + Signature { params, results } } @@ -117,19 +120,18 @@ impl ComponentTypesBuilder { ) -> Signature { let lower_ty = &self[lower.ty]; let lower_ptr_ty = lower.options.ptr(); - let params = if lower.options.async_ { - vec![lower_ptr_ty] + let max_flat_params = if lower.options.async_ { + MAX_FLAT_ASYNC_PARAMS } else { - match self.flatten_types( - &lower.options, - MAX_FLAT_PARAMS, - self[lower_ty.params].types.iter().copied(), - ) { - Some(list) => list, - None => { - vec![lower_ptr_ty] - } - } + MAX_FLAT_PARAMS + }; + let params = match self.flatten_types( + &lower.options, + max_flat_params, + self[lower_ty.params].types.iter().copied(), + ) { + Some(list) => list, + None => vec![lower_ptr_ty], }; let lift_ty = &self[lift.ty]; @@ -158,13 +160,11 @@ impl ComponentTypesBuilder { options: &Options, tys: impl IntoIterator, ) -> Option> { - if options.async_ { - // When lowering an async function, we always spill parameters to - // linear memory. - None - } else { - self.flatten_types(options, MAX_FLAT_RESULTS, tys) - } + // Async functions "use the stack" for zero return values, meaning + // nothing is actually passed, but otherwise if anything is returned + // it's always through memory. + let max = if options.async_ { 0 } else { MAX_FLAT_RESULTS }; + self.flatten_types(options, max, tys) } pub(super) fn flatten_lifting_types( @@ -214,15 +214,18 @@ impl ComponentTypesBuilder { }; let lower_ty = &self[lower.ty]; + let lower_result_tys = &self[lower_ty.results]; let results = if lower.options.async_ { // Add return pointer - params.push(lift_ptr_ty); + if !lower_result_tys.types.is_empty() { + params.push(lift_ptr_ty); + } Vec::new() } else { match self.flatten_types( &lower.options, MAX_FLAT_RESULTS, - self[lower_ty.results].types.iter().copied(), + lower_result_tys.types.iter().copied(), ) { Some(list) => list, None => { diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 87f0c138a9..7e35a376af 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -17,10 +17,11 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, FixedEncoding as FE, - FlatType, InterfaceType, MAX_FLAT_PARAMS, StringEncoding, Transcode, - TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, - TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, - TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, + FlatType, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT, + PREPARE_ASYNC_WITH_RESULT, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, + TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, + TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, + TypeVariantIndex, VariantInfo, }; use crate::fact::signature::Signature; use crate::fact::transcode::Transcoder; @@ -31,6 +32,7 @@ use crate::fact::{ }; use crate::prelude::*; use crate::{FuncIndex, GlobalIndex}; +use cranelift_entity::Signed; use std::collections::HashMap; use std::mem; use std::ops::Range; @@ -186,12 +188,13 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // point a `STATUS_RETURNED` event will be delivered to the caller. let start = async_start_adapter(module); let return_ = async_return_adapter(module); - let (compiler, _, lift_sig) = compiler(module, adapter); + let (compiler, lower_sig, lift_sig) = compiler(module, adapter); compiler.compile_async_to_async_adapter( adapter, start, return_, i32::try_from(lift_sig.params.len()).unwrap(), + &lower_sig, ); } (false, true) => { @@ -241,13 +244,14 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { let lift_sig = module.types.signature(&adapter.lift, Context::Lift); let start = async_start_adapter(module); let return_ = async_return_adapter(module); - let (compiler, ..) = compiler(module, adapter); + let (compiler, lower_sig, ..) = 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(), + &lower_sig, ); } } @@ -418,40 +422,13 @@ impl<'a, 'b> Compiler<'a, 'b> { start: FunctionId, return_: FunctionId, param_count: i32, + lower_sig: &Signature, ) { - let prepare = self - .module - .import_async_prepare_call(&adapter.name, adapter.lift.options.memory); let start_call = self.module .import_async_start_call(&adapter.name, 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(adapter.lift.instance.as_u32()).unwrap(), - )); - self.instruction(I32Const( - i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(), - )); - self.instruction(I32Const(i32::from( - adapter.lift.options.string_encoding as u8, - ))); - // Async-lowered imports pass params and receive results via linear - // memory, and those pointers are in the the first and second params to - // this adapter. We pass them on to the host so it can store them in - // the subtask for later use. - self.instruction(LocalGet(0)); - self.instruction(LocalGet(1)); - self.instruction(Call(prepare.as_u32())); + self.call_prepare(adapter, start, return_, lower_sig, false); // TODO: As an optimization, consider checking the backpressure flag on // the callee instance and, if it's unset _and_ the callee uses a @@ -478,33 +455,30 @@ impl<'a, 'b> Compiler<'a, 'b> { self.finish() } - /// Compile an adapter function supporting a sync-lowered import to an - /// async-lifted export. + /// Invokes the `prepare_call` builtin with the provided parameters for this + /// adapter. /// - /// This uses a pair of `sync-prepare` and `sync-start` built-in functions - /// to set up and start a subtask, respectively. `sync-prepare` accepts - /// `start` and `return_` functions which copy the parameters and results, - /// respectively; the host will call the former when the callee has cleared - /// its backpressure flag and the latter when the callee has called - /// `task.return`. - fn compile_sync_to_async_adapter( - mut self, + /// This is part of a {,a}sync -> {,a}sync adapter. This is done to create + /// the task on the host side of the runtime and such. This will notably + /// invoke a Cranelift builtin which will spill all wasm-level parameters to + /// the stack to handle variadic signatures. + /// + /// Note that the `prepare_sync` parameter here configures the + /// `result_count_or_max_if_async` parameter to indicate whether this is a + /// sync or async prepare. + fn call_prepare( + &mut self, adapter: &AdapterData, start: FunctionId, return_: FunctionId, - lift_param_count: i32, lower_sig: &Signature, + prepare_sync: bool, ) { - let prepare = self.module.import_sync_prepare_call( + let prepare = self.module.import_prepare_call( &adapter.name, &lower_sig.params, adapter.lift.options.memory, ); - let start_call = self.module.import_sync_start_call( - &adapter.name, - adapter.lift.options.callback, - &lower_sig.results, - ); self.flush_code(); self.module.funcs[self.result] @@ -525,28 +499,63 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(I32Const(i32::from( adapter.lift.options.string_encoding as u8, ))); - self.instruction(I32Const( - i32::try_from( - self.types - .flatten_types( - &adapter.lower.options, - usize::MAX, - self.types[self.types[adapter.lower.ty].results] - .types - .iter() - .copied(), - ) - .map(|v| v.len()) - .unwrap_or(usize::try_from(i32::MAX).unwrap()), - ) - .unwrap(), - )); + // flag this as a preparation for either an async call or sync call, + // depending on `prepare_sync` + let result_types = &self.types[self.types[adapter.lower.ty].results].types; + if prepare_sync { + self.instruction(I32Const( + i32::try_from( + self.types + .flatten_types( + &adapter.lower.options, + usize::MAX, + result_types.iter().copied(), + ) + .map(|v| v.len()) + .unwrap_or(usize::try_from(i32::MAX).unwrap()), + ) + .unwrap(), + )); + } else { + if result_types.len() > 0 { + self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.signed())); + } else { + self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.signed())); + } + } + + // forward all our own arguments on to the host stub for index in 0..lower_sig.params.len() { self.instruction(LocalGet(u32::try_from(index).unwrap())); } - self.instruction(Call(prepare.as_u32())); + } + + /// Compile an adapter function supporting a sync-lowered import to an + /// async-lifted export. + /// + /// This uses a pair of `sync-prepare` and `sync-start` built-in functions + /// to set up and start a subtask, respectively. `sync-prepare` accepts + /// `start` and `return_` functions which copy the parameters and results, + /// respectively; the host will call the former when the callee has cleared + /// its backpressure flag and the latter when the callee has called + /// `task.return`. + fn compile_sync_to_async_adapter( + mut self, + adapter: &AdapterData, + start: FunctionId, + return_: FunctionId, + lift_param_count: i32, + lower_sig: &Signature, + ) { + let start_call = self.module.import_sync_start_call( + &adapter.name, + adapter.lift.options.callback, + &lower_sig.results, + ); + + self.call_prepare(adapter, start, return_, lower_sig, true); // TODO: As an optimization, consider checking the backpressure flag on // the callee instance and, if it's unset _and_ the callee uses a @@ -584,36 +593,13 @@ impl<'a, 'b> Compiler<'a, 'b> { return_: FunctionId, param_count: i32, result_count: i32, + lower_sig: &Signature, ) { - let prepare = self - .module - .import_async_prepare_call(&adapter.name, adapter.lift.options.memory); let start_call = self.module .import_async_start_call(&adapter.name, 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(adapter.lift.instance.as_u32()).unwrap(), - )); - self.instruction(I32Const( - i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(), - )); - self.instruction(I32Const(i32::from( - adapter.lift.options.string_encoding as u8, - ))); - self.instruction(LocalGet(0)); - self.instruction(LocalGet(1)); - self.instruction(Call(prepare.as_u32())); + self.call_prepare(adapter, start, return_, lower_sig, false); // We export this function so we can pass a funcref to the host. // @@ -809,12 +795,17 @@ impl<'a, 'b> Compiler<'a, 'b> { // TODO: handle subtyping assert_eq!(src_tys.len(), dst_tys.len()); - let src_flat = if adapter.lower.options.async_ { - None + // Async lowered functions have a smaller limit on flat parameters, but + // their destination, a lifted function, does not have a different limit + // than sync functions. + let max_flat_params = if adapter.lower.options.async_ { + MAX_FLAT_ASYNC_PARAMS } else { - self.types - .flatten_types(lower_opts, MAX_FLAT_PARAMS, src_tys.iter().copied()) + MAX_FLAT_PARAMS }; + let src_flat = + self.types + .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()); diff --git a/crates/test-programs/src/bin/async_cancel_callee.rs b/crates/test-programs/src/bin/async_cancel_callee.rs index fd5eb8f962..7498ccd224 100644 --- a/crates/test-programs/src/bin/async_cancel_callee.rs +++ b/crates/test-programs/src/bin/async_cancel_callee.rs @@ -6,7 +6,6 @@ mod bindings { } use { - std::ptr, test_programs::async_::{ CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_CANCELLED, EVENT_NONE, EVENT_SUBTASK, STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_STARTED, context_get, context_set, @@ -42,10 +41,10 @@ unsafe fn sleep_millis(_: u64) { #[link(wasm_import_module = "local:local/sleep")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - fn sleep_millis_async(_: *mut u8, _: *mut u8) -> u32; + fn sleep_millis_async(ms: u64) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe fn sleep_millis_async(_: *mut u8, _: *mut u8) -> u32 { +unsafe fn sleep_millis_async(ms: u64) -> u32 { unreachable!() } @@ -74,13 +73,11 @@ enum State { set: u32, waitable: u32, params: SleepParams, - param_ptr: *mut u64, }, S2 { set: u32, waitable: u32, params: SleepParams, - param_ptr: *mut u64, }, } @@ -131,9 +128,7 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( State::S0(params) => { assert_eq!(event0, EVENT_NONE); - let param_ptr = Box::into_raw(Box::new(params.time_in_millis)); - - let status = sleep_millis_async(param_ptr.cast(), ptr::null_mut()); + let status = sleep_millis_async(params.time_in_millis); let waitable = status >> 4; let status = status & 0xF; @@ -147,7 +142,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params: *params, - param_ptr, }; CALLBACK_CODE_WAIT | (set << 4) @@ -157,7 +151,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params, - param_ptr, } => { assert_eq!(event0, EVENT_CANCELLED); @@ -194,9 +187,7 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( CALLBACK_CODE_EXIT } else { - **param_ptr = params.on_cancel_delay_millis; - - let status = sleep_millis_async(param_ptr.cast(), ptr::null_mut()); + let status = sleep_millis_async(params.on_cancel_delay_millis); let waitable = status >> 4; let status = status & 0xF; @@ -211,7 +202,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params: *params, - param_ptr: *param_ptr, }; CALLBACK_CODE_WAIT | (set << 4) @@ -222,7 +212,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params, - param_ptr, } => { assert_eq!(event0, EVENT_SUBTASK); assert_eq!(event1, *waitable); @@ -237,7 +226,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( waitable_join(*waitable, 0); subtask_drop(*waitable); waitable_set_drop(*set); - drop(Box::from_raw(*param_ptr)); match params.on_cancel { ON_CANCEL_TASK_RETURN => task_return_sleep_millis(), diff --git a/crates/test-programs/src/bin/async_cancel_caller.rs b/crates/test-programs/src/bin/async_cancel_caller.rs index 38bcead301..5f013839c2 100644 --- a/crates/test-programs/src/bin/async_cancel_caller.rs +++ b/crates/test-programs/src/bin/async_cancel_caller.rs @@ -5,14 +5,11 @@ mod bindings { }); } -use { - std::ptr, - test_programs::async_::{ - BLOCKED, CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_NONE, EVENT_SUBTASK, - STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_START_CANCELLED, STATUS_STARTED, - STATUS_STARTING, context_get, context_set, subtask_cancel, subtask_cancel_async, - subtask_drop, waitable_join, waitable_set_drop, waitable_set_new, - }, +use test_programs::async_::{ + BLOCKED, CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_NONE, EVENT_SUBTASK, + STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_START_CANCELLED, STATUS_STARTED, + STATUS_STARTING, context_get, context_set, subtask_cancel, subtask_cancel_async, subtask_drop, + waitable_join, waitable_set_drop, waitable_set_new, }; #[cfg(target_arch = "wasm32")] @@ -42,10 +39,10 @@ mod sleep { #[link(wasm_import_module = "local:local/sleep")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - pub fn sleep_millis(_: *mut u8, _: *mut u8) -> u32; + pub fn sleep_millis(_: u64) -> u32; } #[cfg(not(target_arch = "wasm32"))] - pub unsafe fn sleep_millis(_: *mut u8, _: *mut u8) -> u32 { + pub unsafe fn sleep_millis(_: u64) -> u32 { unreachable!() } } @@ -55,10 +52,10 @@ mod sleep_with_options { #[link(wasm_import_module = "local:local/sleep-with-options")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - pub fn sleep_millis(_: *mut u8, _: *mut u8) -> u32; + pub fn sleep_millis(_: *mut u8) -> u32; } #[cfg(not(target_arch = "wasm32"))] - pub unsafe fn sleep_millis(_: *mut u8, _: *mut u8) -> u32 { + pub unsafe fn sleep_millis(_: *mut u8) -> u32 { unreachable!() } } @@ -107,7 +104,6 @@ enum State { set: u32, waitable: u32, params: *mut SleepParams, - params2: *mut u64, }, } @@ -141,7 +137,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 mode: *mode, })); - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -167,7 +163,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set_backpressure(false); - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -193,7 +189,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (*params).on_cancel_delay_millis = 10; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -237,7 +233,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).on_cancel = ON_CANCEL_TASK_RETURN; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -288,7 +284,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).on_cancel = ON_CANCEL_TASK_CANCEL; (**params).synchronous_delay = true; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -337,9 +333,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 // `STATUS_RETURNED` when complete since the callee cannot // actually be cancelled. - let params2 = Box::into_raw(Box::new(10u64)); - - let status = sleep::sleep_millis(params2.cast(), ptr::null_mut()); + let status = sleep::sleep_millis(10); let waitable = status >> 4; let status = status & 0xF; @@ -358,7 +352,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set, waitable, params: *params, - params2, }; CALLBACK_CODE_WAIT | (set << 4) @@ -368,7 +361,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set, waitable, params, - params2, } => { assert_eq!(event0, EVENT_SUBTASK); assert_eq!(event1, *waitable); @@ -377,7 +369,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 waitable_join(*waitable, 0); subtask_drop(*waitable); waitable_set_drop(*set); - drop(Box::from_raw(*params2)); // Next, call and cancel `sleep_with_options::sleep_millis` with // a non-zero cancel delay, and specify that the callee should @@ -387,7 +378,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).synchronous_delay = true; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -406,7 +397,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).synchronous_delay = false; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; diff --git a/crates/test-programs/src/bin/async_poll_stackless.rs b/crates/test-programs/src/bin/async_poll_stackless.rs index 6277783e74..7ad3908a3e 100644 --- a/crates/test-programs/src/bin/async_poll_stackless.rs +++ b/crates/test-programs/src/bin/async_poll_stackless.rs @@ -37,9 +37,9 @@ fn async_when_ready() -> u32 { #[link(wasm_import_module = "local:local/ready")] unsafe extern "C" { #[link_name = "[async-lower][async]when-ready"] - fn call_when_ready(_: *mut u8, _: *mut u8) -> u32; + fn call_when_ready() -> u32; } - unsafe { call_when_ready(std::ptr::null_mut(), std::ptr::null_mut()) } + unsafe { call_when_ready() } } } diff --git a/crates/test-programs/src/bin/async_poll_synchronous.rs b/crates/test-programs/src/bin/async_poll_synchronous.rs index 6f1689ed18..962b5e7ba7 100644 --- a/crates/test-programs/src/bin/async_poll_synchronous.rs +++ b/crates/test-programs/src/bin/async_poll_synchronous.rs @@ -28,9 +28,9 @@ fn async_when_ready() -> u32 { #[link(wasm_import_module = "local:local/ready")] unsafe extern "C" { #[link_name = "[async-lower][async]when-ready"] - fn call_when_ready(_: *mut u8, _: *mut u8) -> u32; + fn call_when_ready() -> u32; } - unsafe { call_when_ready(std::ptr::null_mut(), std::ptr::null_mut()) } + unsafe { call_when_ready() } } } diff --git a/crates/test-programs/src/bin/async_round_trip_stackful.rs b/crates/test-programs/src/bin/async_round_trip_stackful.rs index 991ad6e065..e11ddf6191 100644 --- a/crates/test-programs/src/bin/async_round_trip_stackful.rs +++ b/crates/test-programs/src/bin/async_round_trip_stackful.rs @@ -43,10 +43,10 @@ unsafe extern "C" fn task_return_foo(_ptr: *mut u8, _len: usize) { #[link(wasm_import_module = "local:local/baz")] unsafe extern "C" { #[link_name = "[async-lower][async]foo"] - fn import_foo(params: *mut u8, results: *mut u8) -> u32; + fn import_foo(ptr: *mut u8, len: usize, results: *mut u8) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe extern "C" fn import_foo(_params: *mut u8, _results: *mut u8) -> u32 { +unsafe extern "C" fn import_foo(ptr: *mut u8, len: usize, _results: *mut u8) -> u32 { unreachable!() } @@ -65,13 +65,9 @@ unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) { 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 result = import_foo(s.as_ptr().cast_mut(), s.len(), results); let mut status = result & 0xf; let call = result >> 4; let set = waitable_set_new(); @@ -93,7 +89,6 @@ unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) { } } } - alloc::dealloc(params, layout); let len = *results.add(4).cast::(); let s = format!( diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index cf813b7f23..ba9ef17af1 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -96,7 +96,8 @@ use { wasmtime_environ::{ PrimaryMap, component::{ - MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, RuntimeComponentInstanceIndex, StringEncoding, + MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, + RuntimeComponentInstanceIndex, StringEncoding, TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex, }, @@ -702,7 +703,10 @@ enum WaitableState { /// guest->guest call orchestrated by a fused adapter. enum CallerInfo { /// Metadata for a call to an async-lowered import - Async { params: u32, results: u32 }, + Async { + params: Vec, + has_result: bool, + }, /// Metadata for a call to an sync-lowered import Sync { params: Vec, @@ -1545,7 +1549,15 @@ impl ComponentInstance { } let result_info = match &caller_info { - CallerInfo::Async { results, .. } => ResultInfo::Heap { results: *results }, + CallerInfo::Async { + has_result: true, + params, + } => ResultInfo::Heap { + results: params.last().unwrap().get_u32(), + }, + CallerInfo::Async { + has_result: false, .. + } => ResultInfo::Stack { result_count: 0 }, CallerInfo::Sync { result_count, params, @@ -1573,16 +1585,22 @@ impl ComponentInstance { assert!(dst.len() <= MAX_FLAT_PARAMS); let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; let count = match caller_info { - CallerInfo::Async { params, .. } => { - src[0] = MaybeUninit::new(ValRaw::u32(params)); - 1 + // Async callers, if they have a result, use the last + // parameter as a return pointer so chop that off if + // relevant here. + CallerInfo::Async { params, has_result } => { + let params = ¶ms[..params.len() - usize::from(has_result)]; + for (param, src) in params.iter().zip(&mut src) { + src.write(*param); + } + params.len() } + + // Sync callers forward everything directly. CallerInfo::Sync { params, .. } => { - // SAFETY: Transmuting from `&[T]` to - // `&[MaybeUninit]` should be sound for any `T`. - src[..params.len()].copy_from_slice(unsafe { - mem::transmute::<&[ValRaw], &[MaybeUninit]>(¶ms) - }); + for (param, src) in params.iter().zip(&mut src) { + src.write(*param); + } params.len() } }; @@ -3441,7 +3459,7 @@ impl Instance { pub trait VMComponentAsyncStore { /// A helper function for fused adapter modules involving calls where the /// caller is sync-lowered but the callee is async-lifted. - unsafe fn sync_prepare( + unsafe fn prepare_call( &mut self, instance: &mut ComponentInstance, memory: *mut VMMemoryDefinition, @@ -3468,22 +3486,6 @@ pub trait VMComponentAsyncStore { storage_len: usize, ) -> Result<()>; - /// A helper function for fused adapter modules involving calls where the - /// caller is async-lowered. - unsafe fn async_prepare( - &mut self, - instance: &mut ComponentInstance, - memory: *mut VMMemoryDefinition, - start: *mut VMFuncRef, - return_: *mut VMFuncRef, - caller_instance: RuntimeComponentInstanceIndex, - callee_instance: RuntimeComponentInstanceIndex, - task_return_type: TypeTupleIndex, - string_encoding: u8, - params: u32, - results: u32, - ) -> Result<()>; - /// A helper function for fused adapter modules involving calls where the /// caller is async-lowered. unsafe fn async_start( @@ -3598,7 +3600,7 @@ pub trait VMComponentAsyncStore { /// SAFETY: See trait docs. impl VMComponentAsyncStore for StoreInner { - unsafe fn sync_prepare( + unsafe fn prepare_call( &mut self, instance: &mut ComponentInstance, memory: *mut VMMemoryDefinition, @@ -3608,10 +3610,15 @@ impl VMComponentAsyncStore for StoreInner { callee_instance: RuntimeComponentInstanceIndex, task_return_type: TypeTupleIndex, string_encoding: u8, - result_count: u32, + result_count_or_max_if_async: u32, storage: *mut ValRaw, storage_len: usize, ) -> Result<()> { + // SAFETY: The `wasmtime_cranelift`-generated code that calls + // this method will have ensured that `storage` is a valid + // pointer containing at least `storage_len` items. + let params = unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(); + instance.prepare_call( StoreContextMut(self), start, @@ -3621,12 +3628,19 @@ impl VMComponentAsyncStore for StoreInner { task_return_type, memory, string_encoding, - CallerInfo::Sync { - // SAFETY: The `wasmtime_cranelift`-generated code that calls - // this method will have ensured that `storage` is a valid - // pointer containing at least `storage_len` items. - params: unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(), - result_count, + match result_count_or_max_if_async { + PREPARE_ASYNC_NO_RESULT => CallerInfo::Async { + params, + has_result: false, + }, + PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async { + params, + has_result: true, + }, + result_count => CallerInfo::Sync { + params, + result_count, + }, }, ) } @@ -3657,32 +3671,6 @@ impl VMComponentAsyncStore for StoreInner { .map(drop) } - unsafe fn async_prepare( - &mut self, - instance: &mut ComponentInstance, - memory: *mut VMMemoryDefinition, - start: *mut VMFuncRef, - return_: *mut VMFuncRef, - caller_instance: RuntimeComponentInstanceIndex, - callee_instance: RuntimeComponentInstanceIndex, - task_return_type: TypeTupleIndex, - string_encoding: u8, - params: u32, - results: u32, - ) -> Result<()> { - instance.prepare_call( - StoreContextMut(self), - start, - return_, - caller_instance, - callee_instance, - task_return_type, - memory, - string_encoding, - CallerInfo::Async { params, results }, - ) - } - unsafe fn async_start( &mut self, instance: &mut ComponentInstance, diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index e6a2aaaf55..9391d47b78 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -19,8 +19,8 @@ use core::mem::{self, MaybeUninit}; use core::pin::Pin; use core::ptr::NonNull; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, - RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, + CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, + MAX_FLAT_RESULTS, RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, TypeTuple, }; pub struct HostFunc { @@ -269,24 +269,6 @@ where Params: Lift + Send + Sync + 'static, 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 - /// return pointer. - #[repr(C)] - struct ReturnPointer { - args: T, - retptr: ValRaw, - } - - /// Representation of arguments to this function when the return value is - /// returned directly, namely the arguments and return value all start from - /// the beginning (aka this is a `union`, not a `struct`). - #[repr(C)] - union ReturnStack { - args: T, - ret: U, - } - let options = Options::new( store.0.store_opaque().id(), NonNull::new(memory), @@ -310,21 +292,37 @@ where if async_ { #[cfg(feature = "component-model-async")] { - let paramptr = storage[0].assume_init(); - let retptr = storage[1].assume_init(); + let mut storage = Storage::<'_, Params, u32>::new_async::(storage); + // Lift the parameters, either from flat storage or from linear + // memory. let params = { let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); lift.enter_call(); - let ptr = validate_inbounds::(lift.memory(), ¶mptr)?; - Params::load(lift, param_tys, &lift.memory()[ptr..][..Params::SIZE32])? + storage.lift_params(lift, param_tys)? }; - let future = closure(store.as_context_mut(), instance, params); + // Load the return pointer, if present. + let retptr = match storage.async_retptr() { + Some(ptr) => { + let mut lower = + LowerContext::new(store.as_context_mut(), &options, &types, instance); + validate_inbounds::(lower.as_slice_mut(), ptr)? + } + // If there's no return pointer then `Return` should have an + // empty flat representation. In this situation pretend the + // return pointer was 0 so we have something to shepherd along + // into the closure below. + None => { + assert_eq!(Return::flatten_count(), 0); + 0 + } + }; + let future = closure(store.as_context_mut(), instance, params); let instance_ptr = SendSyncPtr::new(NonNull::new(instance).unwrap()); - let task = instance.first_poll(store, future, caller_instance, { + let task = instance.first_poll(store.as_context_mut(), future, caller_instance, { let types = types.clone(); move |mut store: StoreContextMut, instance: &mut ComponentInstance, @@ -333,8 +331,7 @@ where flags.set_may_leave(false); let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); - let ptr = validate_inbounds::(lower.as_slice_mut(), &retptr)?; - ret.store(&mut lower, result_tys, ptr)?; + ret.store(&mut lower, result_tys, retptr)?; flags.set_may_leave(true); lower.exit_call()?; Ok(()) @@ -348,7 +345,8 @@ where Status::Returned.pack(None) }; - storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); + let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); + storage.lower_results(&mut lower, InterfaceType::U32, status)?; } #[cfg(not(feature = "component-model-async"))] { @@ -358,29 +356,7 @@ where ); } } else { - // 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 { - 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 storage = Storage::<'_, Params, Return>::new_sync(storage); let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); lift.enter_call(); let params = storage.lift_params(&mut lift, param_tys)?; @@ -401,11 +377,136 @@ where return Ok(()); + /// Type-level representation of the matrix of possibilities of how + /// WebAssembly parameters and results are handled in the canonical ABI. + /// + /// Wasmtime's ABI here always works with `&mut [MaybeUninit]` as the + /// base representation of params/results. Parameters are passed + /// sequentially and results are returned by overwriting the parameters. + /// That means both params/results start from index 0. + /// + /// The type-level representation here involves working with the typed + /// `P::Lower` and `R::Lower` values which is a type-level representation of + /// a lowered value. All lowered values are in essence a sequence of + /// `ValRaw` values one after the other to fit within this original array + /// that is the basis of Wasmtime's ABI. + /// + /// The various combinations here are cryptic, but only used in this file. + /// This in theory cuts down on the verbosity below, but an explanation of + /// the various acronyms here are: + /// + /// * Pd - params direct - means that parameters are passed directly in + /// their flat representation via `P::Lower`. + /// + /// * Pi - params indirect - means that parameters are passed indirectly in + /// linear memory and the argument here is `ValRaw` to store the pointer. + /// + /// * Rd - results direct - means that results are returned directly in + /// their flat representation via `R::Lower`. Note that this is always + /// represented as `MaybeUninit` as well because the return + /// values may point to uninitialized memory if there were no parameters + /// for example. + /// + /// * Ri - results indirect - means that results are returned indirectly in + /// linear memory through the pointer specified. Note that this is + /// specified as a `ValRaw` to represent the argument that's being given + /// to the host from WebAssembly. + /// + /// * Ar - async results - means that the parameters to this call + /// additionally include an async result pointer. Async results are always + /// transmitted via a pointer so this is always a `ValRaw`. + /// + /// Internally this type makes liberal use of `Union` and `Pair` helpers + /// below which are simple `#[repr(C)]` wrappers around a pair of types that + /// are a union or a pair. + /// + /// Note that for any combination of `P` and `R` this `enum` is actually + /// pointless as a single variant will be used. In theory we should be able + /// to monomorphize based on `P` and `R` to a specific type. This + /// monomorphization depends on conditionals like `flatten_count() <= N`, + /// however, and I don't know how to encode that in Rust easily. In lieu of + /// that we assume LLVM will figure things out and boil away the actual enum + /// and runtime dispatch. enum Storage<'a, P: ComponentType, R: ComponentType> { - Direct(&'a mut MaybeUninit>), - ParamsIndirect(&'a mut MaybeUninit>), - ResultsIndirect(&'a ReturnPointer), - Indirect(&'a ReturnPointer), + /// Params: direct, Results: direct + /// + /// The lowered representation of params/results are overlaid on top of + /// each other. + PdRd(&'a mut Union>), + + /// Params: direct, Results: indirect + /// + /// The return pointer comes after the params so this is sequentially + /// laid out with one after the other. + PdRi(&'a Pair), + + /// Params: indirect, Results: direct + /// + /// Here the return values are overlaid on top of the pointer parameter. + PiRd(&'a mut Union>), + + /// Params: indirect, Results: indirect + /// + /// Here the two parameters are laid out sequentially one after the + /// other. + PiRi(&'a Pair), + + /// Params: direct + async result, Results: direct + /// + /// This is like `PdRd` except that the parameters additionally include + /// a pointer for where to store the result. + PdArRd(&'a mut Union, MaybeUninit>), + + /// Params: indirect + async result, Results: direct + /// + /// This is like `PiRd` except that the parameters additionally include + /// a pointer for where to store the result. + PiArRd(&'a mut Union, MaybeUninit>), + } + + // Helper structure used above in `Storage` to represent two consecutive + // values. + #[repr(C)] + #[derive(Copy, Clone)] + struct Pair { + a: T, + b: U, + } + + // Helper structure used above in `Storage` to represent two values overlaid + // on each other. + #[repr(C)] + union Union { + a: T, + b: U, + } + + /// Representation of where parameters are lifted from. + enum Src<'a, T> { + /// Parameters are directly lifted from `T`, which is under the hood a + /// sequence of `ValRaw`. This is `P::Lower` for example. + Direct(&'a T), + + /// Parameters are loaded from linear memory, and this is the wasm + /// parameter representing the pointer into linear memory to load from. + Indirect(&'a ValRaw), + } + + /// Dual of [`Src`], where to store results. + enum Dst<'a, T> { + /// Results are stored directly in this pointer. + /// + /// Note that this is a mutable pointer but it's specifically + /// `MaybeUninit` as trampolines do not initialize it. The `T` here will + /// be `R::Lower` for example. + Direct(&'a mut MaybeUninit), + + /// Results are stored in linear memory, and this value is the wasm + /// parameter given which represents the pointer into linear memory. + /// + /// Note that this is not mutable as the parameter is not mutated, but + /// memory will be mutated. + Indirect(&'a ValRaw), } impl Storage<'_, P, R> @@ -413,39 +514,152 @@ where P: ComponentType + Lift, R: ComponentType + Lower, { - unsafe fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result

{ - match self { - Storage::Direct(storage) => P::lift(cx, ty, &storage.assume_init_ref().args), - Storage::ResultsIndirect(storage) => P::lift(cx, ty, &storage.args), - Storage::ParamsIndirect(storage) => { - let ptr = validate_inbounds::

(cx.memory(), &storage.assume_init_ref().args)?; - P::load(cx, ty, &cx.memory()[ptr..][..P::SIZE32]) + /// Classifies a new `Storage` suitable for use with sync functions. + /// + /// 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. + /// + /// # Safety + /// + /// Requires that the `storage` provided does indeed match an wasm + /// function with the signature of `P` and `R` as params/results. + unsafe fn new_sync(storage: &mut [MaybeUninit]) -> Storage<'_, P, R> { + // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers + // used which view the slice provided as a different type. This + // safety should be upheld by the contract of the `ComponentType` + // trait and its `Lower` type parameter meaning they're valid to + // view as a sequence of `ValRaw` types. Additionally the + // `ComponentType` trait ensures that the matching of the runtime + // length of `storage` should match the actual size of `P::Lower` + // and `R::Lower` or such as needed. + unsafe { + if P::flatten_count() <= MAX_FLAT_PARAMS { + if R::flatten_count() <= MAX_FLAT_RESULTS { + Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref()) + } + } else { + if R::flatten_count() <= MAX_FLAT_RESULTS { + Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref()) + } } - Storage::Indirect(storage) => { - let ptr = validate_inbounds::

(cx.memory(), &storage.args)?; + } + } + + fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result

{ + match self.lift_src() { + Src::Direct(storage) => P::lift(cx, ty, storage), + Src::Indirect(ptr) => { + let ptr = validate_inbounds::

(cx.memory(), ptr)?; P::load(cx, ty, &cx.memory()[ptr..][..P::SIZE32]) } } } - unsafe fn lower_results( + fn lift_src(&self) -> Src<'_, P::Lower> { + match self { + // SAFETY: these `unsafe` blocks are due to accessing union + // fields. The safety here relies on the contract of the + // `ComponentType` trait which should ensure that the types + // projected onto a list of wasm parameters are indeed correct. + // That means that the projections here, if the types are + // correct, all line up to initialized memory that's well-typed + // to access. + Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) }, + Storage::PdRi(storage) => Src::Direct(&storage.a), + Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) }, + Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) }, + Storage::PiRi(storage) => Src::Indirect(&storage.a), + Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) }, + } + } + + fn lower_results( &mut self, cx: &mut LowerContext<'_, T>, ty: InterfaceType, ret: R, ) -> Result<()> { - match self { - Storage::Direct(storage) => ret.lower(cx, ty, map_maybe_uninit!(storage.ret)), - Storage::ParamsIndirect(storage) => { - ret.lower(cx, ty, map_maybe_uninit!(storage.ret)) - } - Storage::ResultsIndirect(storage) => { - let ptr = validate_inbounds::(cx.as_slice_mut(), &storage.retptr)?; + match self.lower_dst() { + Dst::Direct(storage) => ret.lower(cx, ty, storage), + Dst::Indirect(ptr) => { + let ptr = validate_inbounds::(cx.as_slice_mut(), ptr)?; ret.store(cx, ty, ptr) } - Storage::Indirect(storage) => { - let ptr = validate_inbounds::(cx.as_slice_mut(), &storage.retptr)?; - ret.store(cx, ty, ptr) + } + } + + fn lower_dst(&mut self) -> Dst<'_, R::Lower> { + match self { + // SAFETY: these unsafe blocks are due to accessing fields of a + // `union` which is not safe in Rust. The returned value is + // `MaybeUninit` in all cases, however, which should + // safely model how `union` memory is possibly uninitialized. + // Additionally `R::Lower` has the `unsafe` contract that all + // its bit patterns must be sound, which additionally should + // help make this safe. + Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PdRi(storage) => Dst::Indirect(&storage.b), + Storage::PiRi(storage) => Dst::Indirect(&storage.b), + } + } + + fn async_retptr(&self) -> Option<&ValRaw> { + match self { + // SAFETY: like above these are `unsafe` due to accessing a + // `union` field. This should be safe via the construction of + // `Storage` which should correctly determine whether or not an + // async return pointer is provided and classify the args/rets + // appropriately. + Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) }, + Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) }, + Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None, + } + } + } + + impl

Storage<'_, P, u32> + where + P: ComponentType + Lift, + { + /// Classifies a new `Storage` suitable for use with async functions. + /// + /// # Safety + /// + /// Requires that the `storage` provided does indeed match an `async` + /// wasm function with the signature of `P` and `R` as params/results. + unsafe fn new_async(storage: &mut [MaybeUninit]) -> Storage<'_, P, u32> + where + R: ComponentType + Lower, + { + // SAFETY: see `Storage::new` for discussion on why this should be + // safe given the unsafe contract of the `ComponentType` trait. + unsafe { + if P::flatten_count() <= MAX_FLAT_ASYNC_PARAMS { + if R::flatten_count() == 0 { + Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut()) + } + } else { + if R::flatten_count() == 0 { + Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut()) + } } } } @@ -529,9 +743,6 @@ where bail!("cannot leave component instance"); } - let args; - let ret_index; - let func_ty = &types[ty]; let param_tys = &types[func_ty.params]; let result_tys = &types[func_ty.results]; @@ -539,25 +750,28 @@ where if async_ { #[cfg(feature = "component-model-async")] { - let paramptr = storage[0].assume_init(); - let retptr = storage[1].assume_init(); - - let params = { + let mut params = Vec::new(); + let ret_index = { let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), &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::>>()? + + dynamic_params_load( + &mut lift, + types, + storage, + param_tys, + &mut params, + MAX_FLAT_ASYNC_PARAMS, + )? + }; + let retptr = if result_tys.types.len() == 0 { + 0 + } else { + let retptr = storage[ret_index].assume_init(); + let mut lower = + LowerContext::new(store.as_context_mut(), &options, &types, instance); + validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)? }; let future = closure( @@ -584,11 +798,7 @@ where let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); - let mut ptr = validate_inbounds_dynamic( - &result_tys.abi, - lower.as_slice_mut(), - &retptr, - )?; + let mut ptr = 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)?; @@ -621,35 +831,15 @@ where } else { let mut cx = LiftContext::new(store.0.store_opaque_mut(), &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 args = Vec::new(); + let ret_index = dynamic_params_load( + &mut cx, + types, + storage, + param_tys, + &mut args, + MAX_FLAT_PARAMS, + )?; let future = closure( store.as_context_mut(), @@ -690,6 +880,46 @@ where return Ok(()); } +/// Loads the parameters for a dynamic host function call into `params` +/// +/// Returns the number of flat `storage` values consumed. +/// +/// # Safety +/// +/// Requires that `param_tys` matches the type signature of the `storage` that +/// was passed in. +unsafe fn dynamic_params_load( + cx: &mut LiftContext<'_>, + types: &ComponentTypes, + storage: &[MaybeUninit], + param_tys: &TypeTuple, + params: &mut Vec, + max_flat_params: usize, +) -> Result { + 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 storage = + unsafe { mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..param_count]) }; + let mut iter = storage.iter(); + for ty in param_tys.types.iter() { + params.push(Val::lift(cx, *ty, &mut iter)?); + } + assert!(iter.next().is_none()); + Ok(param_count) + } else { + let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe { + storage[0].assume_init_ref() + })?; + for ty in param_tys.types.iter() { + 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]; + params.push(Val::load(cx, *ty, memory)?); + } + Ok(1) + } +} + pub(crate) fn validate_inbounds_dynamic( abi: &CanonicalAbiInfo, memory: &[u8], diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 2e393f6c4d..6777d9df40 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -753,7 +753,7 @@ unsafe fn subtask_cancel( } #[cfg(feature = "component-model-async")] -unsafe fn sync_prepare( +unsafe fn prepare_call( vmctx: NonNull, memory: *mut u8, start: *mut u8, @@ -762,12 +762,12 @@ unsafe fn sync_prepare( callee_instance: u32, task_return_type: u32, string_encoding: u32, - result_count: u32, + result_count_or_max_if_async: u32, storage: *mut u8, storage_len: usize, ) -> Result<()> { ComponentInstance::from_vmctx(vmctx, |store, instance| { - store.component_async_store().sync_prepare( + store.component_async_store().prepare_call( instance, memory.cast::(), start.cast::(), @@ -776,7 +776,7 @@ unsafe fn sync_prepare( wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(callee_instance), wasmtime_environ::component::TypeTupleIndex::from_u32(task_return_type), u8::try_from(string_encoding).unwrap(), - result_count, + result_count_or_max_if_async, storage.cast::(), storage_len, ) @@ -804,35 +804,6 @@ unsafe fn sync_start( }) } -#[cfg(feature = "component-model-async")] -unsafe fn async_prepare( - vmctx: NonNull, - memory: *mut u8, - start: *mut u8, - return_: *mut u8, - caller_instance: u32, - callee_instance: u32, - task_return_type: u32, - string_encoding: u32, - params: u32, - results: u32, -) -> Result<()> { - ComponentInstance::from_vmctx(vmctx, |store, instance| { - store.component_async_store().async_prepare( - instance, - memory.cast::(), - start.cast::(), - return_.cast::(), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(callee_instance), - wasmtime_environ::component::TypeTupleIndex::from_u32(task_return_type), - u8::try_from(string_encoding).unwrap(), - params, - results, - ) - }) -} - #[cfg(feature = "component-model-async")] unsafe fn async_start( vmctx: NonNull, diff --git a/src/commands/objdump.rs b/src/commands/objdump.rs index dd5ae311bf..cd9b5aa9c4 100644 --- a/src/commands/objdump.rs +++ b/src/commands/objdump.rs @@ -152,7 +152,10 @@ impl ObjdumpCommand { Func::Builtin } else if name.contains("]::function[") { Func::Wasm - } else if name.contains("trampoline") { + } else if name.contains("trampoline") + || name.ends_with("_array_call") + || name.ends_with("_wasm_call") + { Func::Trampoline } else if name.contains("libcall") || name.starts_with("component") { Func::Libcall diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index f7d60ebdca..ddd96932c6 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -249,11 +249,9 @@ mod one_import_concurrent { ) (core instance $libc-instance (instantiate $libc)) (core module $m - (import "" "foo" (func $foo (param i32 i32) (result i32))) + (import "" "foo" (func $foo (param) (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 diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 0b57ed6947..660cdf5976 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -505,10 +505,8 @@ async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { (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)) + (call $f1 (i32.const 1) (local.get $results)) drop (i32.load offset=0 (local.get $results)) i32.const 2 @@ -530,10 +528,8 @@ async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { 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)) + (call $f3 (i32.const 8) (local.get $results)) drop (call $validate_string_ret (local.get $results)) end diff --git a/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast b/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast index dc340c3bc4..52c8e1209d 100644 --- a/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast +++ b/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast @@ -48,7 +48,7 @@ (core func $waitable-set.wait (canon waitable-set.wait (memory $libc "mem"))) (core module $m - (import "" "f" (func $f (param i32 i32) (result i32))) + (import "" "f" (func $f (result i32))) (import "" "turn-on-backpressure" (func $turn-on-backpressure)) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable.join" (func $waitable.join (param i32 i32))) @@ -59,7 +59,7 @@ (local $set i32) call $turn-on-backpressure - (local.set $status (call $f (i32.const 0) (i32.const 0))) + (local.set $status (call $f)) ;; low 4 bits should be "STARTING == 0" (i32.ne diff --git a/tests/misc_testsuite/component-model-async/drop-subtask.wast b/tests/misc_testsuite/component-model-async/drop-subtask.wast index b8b5765847..3fb3d099f7 100644 --- a/tests/misc_testsuite/component-model-async/drop-subtask.wast +++ b/tests/misc_testsuite/component-model-async/drop-subtask.wast @@ -69,14 +69,14 @@ (import "" "waitable.join" (func $waitable.join (param i32 i32))) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) - (import "" "loop" (func $loop (param i32 i32) (result i32))) + (import "" "loop" (func $loop (result i32))) (import "" "return" (func $return)) (func $drop-after-return (export "drop-after-return") (result i32) (local $ret i32) (local $ws i32) (local $subtask i32) ;; start 'loop' - (local.set $ret (call $loop (i32.const 0xdead) (i32.const 0xbeef))) + (local.set $ret (call $loop)) (if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf))) (then unreachable)) (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4))) diff --git a/tests/misc_testsuite/component-model-async/drop-waitable-set.wast b/tests/misc_testsuite/component-model-async/drop-waitable-set.wast index d2da838dbe..ea78cb30f0 100644 --- a/tests/misc_testsuite/component-model-async/drop-waitable-set.wast +++ b/tests/misc_testsuite/component-model-async/drop-waitable-set.wast @@ -18,7 +18,7 @@ (global $ws (mut i32) (i32.const 0)) (func $start (global.set $ws (call $waitable-set.new))) (start $start) - + (func $wait-on-set (export "wait-on-set") (result i32) ;; wait on $ws (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))) @@ -57,14 +57,14 @@ (core instance $memory (instantiate $Memory)) (core module $Core (import "" "mem" (memory 1)) - (import "" "wait-on-set" (func $wait-on-set (param i32 i32) (result i32))) + (import "" "wait-on-set" (func $wait-on-set (result i32))) (import "" "drop-while-waiting" (func $drop-while-waiting)) (func $run (export "run") (result i32) (local $ret i32) ;; start an async call to 'wait-on-set' which blocks, waiting on a ;; waitable-set. - (local.set $ret (call $wait-on-set (i32.const 0xdeadbeef) (i32.const 0xdeadbeef))) + (local.set $ret (call $wait-on-set)) (if (i32.ne (i32.const 0x11) (local.get $ret)) (then unreachable)) diff --git a/tests/misc_testsuite/component-model-async/empty-wait.wast b/tests/misc_testsuite/component-model-async/empty-wait.wast index 83a53f4b30..c9d5cae496 100644 --- a/tests/misc_testsuite/component-model-async/empty-wait.wast +++ b/tests/misc_testsuite/component-model-async/empty-wait.wast @@ -136,8 +136,8 @@ (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) (import "" "subtask.drop" (func $subtask.drop (param i32))) - (import "" "blocker" (func $blocker (param i32 i32) (result i32))) - (import "" "unblocker" (func $unblocker (param i32 i32) (result i32))) + (import "" "blocker" (func $blocker (param i32) (result i32))) + (import "" "unblocker" (func $unblocker (param i32) (result i32))) (global $ws (mut i32) (i32.const 0)) (func $start (global.set $ws (call $waitable-set.new))) @@ -150,7 +150,7 @@ ;; call 'blocker'; it should block (local.set $retp1 (i32.const 4)) - (local.set $ret (call $blocker (i32.const 0xdeadbeef) (local.get $retp1))) + (local.set $ret (call $blocker (local.get $retp1))) (if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf))) (then unreachable)) (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4))) @@ -159,7 +159,7 @@ ;; call 'unblocker' to unblock 'blocker'; it should complete eagerly (local.set $retp2 (i32.const 8)) - (local.set $ret (call $unblocker (i32.const 0xbeefdead) (local.get $retp2))) + (local.set $ret (call $unblocker (local.get $retp2))) (if (i32.ne (i32.const 2 (; RETURNED ;)) (local.get $ret)) (then unreachable)) (if (i32.ne (i32.const 43) (i32.load (local.get $retp2))) diff --git a/tests/misc_testsuite/component-model-async/fused.wast b/tests/misc_testsuite/component-model-async/fused.wast index 56b9b12c7d..00e8a8d9d3 100644 --- a/tests/misc_testsuite/component-model-async/fused.wast +++ b/tests/misc_testsuite/component-model-async/fused.wast @@ -31,8 +31,7 @@ (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)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable @@ -84,8 +83,7 @@ (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)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable @@ -130,8 +128,7 @@ (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)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable diff --git a/tests/misc_testsuite/component-model-async/wait-forever2.wast b/tests/misc_testsuite/component-model-async/wait-forever2.wast index 80b29e3885..e6df132f05 100644 --- a/tests/misc_testsuite/component-model-async/wait-forever2.wast +++ b/tests/misc_testsuite/component-model-async/wait-forever2.wast @@ -42,12 +42,12 @@ (import "" "waitable.join" (func $waitable.join (param i32 i32))) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) - (import "" "f" (func $f (param i32 i32) (result i32))) + (import "" "f" (func $f (param i32) (result i32))) (func (export "g") (result i32) (local $ws i32) (local $ret i32) (local $subtaski i32) (local.set $ws (call $waitable-set.new)) - (local.set $ret (call $f (i32.const 0) (i32.const 0))) + (local.set $ret (call $f (i32.const 0))) (local.set $subtaski (i32.shr_u (local.get $ret) (i32.const 4))) (call $waitable.join (local.get $subtaski) (local.get $ws)) (call $waitable-set.wait (local.get $ws) (i32.const 0)) From da23082dee0e3dfcecd9ac0e613231c464e3bcea Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 21 May 2025 17:50:59 -0700 Subject: [PATCH 02/12] Fix configured build --- .../wasmtime/src/runtime/component/func/host.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index 9391d47b78..7340adef77 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -19,8 +19,8 @@ use core::mem::{self, MaybeUninit}; use core::pin::Pin; use core::ptr::NonNull; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, - MAX_FLAT_RESULTS, RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, TypeTuple, + CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, + RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, TypeTuple, }; pub struct HostFunc { @@ -455,12 +455,14 @@ where /// /// This is like `PdRd` except that the parameters additionally include /// a pointer for where to store the result. + #[cfg(feature = "component-model-async")] PdArRd(&'a mut Union, MaybeUninit>), /// Params: indirect + async result, Results: direct /// /// This is like `PiRd` except that the parameters additionally include /// a pointer for where to store the result. + #[cfg(feature = "component-model-async")] PiArRd(&'a mut Union, MaybeUninit>), } @@ -577,9 +579,11 @@ where // to access. Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) }, Storage::PdRi(storage) => Src::Direct(&storage.a), + #[cfg(feature = "component-model-async")] Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) }, Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) }, Storage::PiRi(storage) => Src::Indirect(&storage.a), + #[cfg(feature = "component-model-async")] Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) }, } } @@ -610,13 +614,16 @@ where // help make this safe. Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + #[cfg(feature = "component-model-async")] Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + #[cfg(feature = "component-model-async")] Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, Storage::PdRi(storage) => Dst::Indirect(&storage.b), Storage::PiRi(storage) => Dst::Indirect(&storage.b), } } + #[cfg(feature = "component-model-async")] fn async_retptr(&self) -> Option<&ValRaw> { match self { // SAFETY: like above these are `unsafe` due to accessing a @@ -631,6 +638,7 @@ where } } + #[cfg(feature = "component-model-async")] impl

Storage<'_, P, u32> where P: ComponentType + Lift, @@ -648,7 +656,7 @@ where // SAFETY: see `Storage::new` for discussion on why this should be // safe given the unsafe contract of the `ComponentType` trait. unsafe { - if P::flatten_count() <= MAX_FLAT_ASYNC_PARAMS { + if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS { if R::flatten_count() == 0 { Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) } else { @@ -762,7 +770,7 @@ where storage, param_tys, &mut params, - MAX_FLAT_ASYNC_PARAMS, + wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS, )? }; let retptr = if result_tys.types.len() == 0 { From ecf760eacb71e99936f4293746f43765c5e02bfd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 May 2025 07:30:23 -0700 Subject: [PATCH 03/12] Fix compiler warnings --- crates/test-programs/src/bin/async_cancel_callee.rs | 2 +- crates/test-programs/src/bin/async_round_trip_stackful.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/test-programs/src/bin/async_cancel_callee.rs b/crates/test-programs/src/bin/async_cancel_callee.rs index 7498ccd224..1379676862 100644 --- a/crates/test-programs/src/bin/async_cancel_callee.rs +++ b/crates/test-programs/src/bin/async_cancel_callee.rs @@ -44,7 +44,7 @@ unsafe extern "C" { fn sleep_millis_async(ms: u64) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe fn sleep_millis_async(ms: u64) -> u32 { +unsafe fn sleep_millis_async(_ms: u64) -> u32 { unreachable!() } diff --git a/crates/test-programs/src/bin/async_round_trip_stackful.rs b/crates/test-programs/src/bin/async_round_trip_stackful.rs index e11ddf6191..381ef68142 100644 --- a/crates/test-programs/src/bin/async_round_trip_stackful.rs +++ b/crates/test-programs/src/bin/async_round_trip_stackful.rs @@ -46,7 +46,7 @@ unsafe extern "C" { fn import_foo(ptr: *mut u8, len: usize, results: *mut u8) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe extern "C" fn import_foo(ptr: *mut u8, len: usize, _results: *mut u8) -> u32 { +unsafe extern "C" fn import_foo(_ptr: *mut u8, _len: usize, _results: *mut u8) -> u32 { unreachable!() } From e5a220ed45504c8a80e4f8d6e3f607a7a9a00adb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2025 09:43:50 -0700 Subject: [PATCH 04/12] Update to wasm-tools merge --- Cargo.lock | 185 ++++++++++++++++++++++++++++++++++++----------------- Cargo.toml | 46 ++++++------- 2 files changed, 151 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3d497d35e..72b1ec2243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -624,7 +624,7 @@ dependencies = [ "tokio", "wasi-http-draft", "wasm-compose", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime", "wasmtime-wasi", ] @@ -3480,7 +3480,7 @@ dependencies = [ "wasmtime", "wasmtime-test-util", "wat", - "wit-component", + "wit-component 0.231.0", ] [[package]] @@ -3897,7 +3897,7 @@ name = "verify-component-adapter" version = "34.0.0" dependencies = [ "anyhow", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wat", ] @@ -4017,7 +4017,7 @@ dependencies = [ "byte-array-literals", "object", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-encoder", + "wasm-encoder 0.231.0", "wit-bindgen-rust-macro", ] @@ -4078,8 +4078,8 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" -version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "anyhow", "heck 0.4.1", @@ -4091,55 +4091,77 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder", - "wasmparser 0.230.0", + "wasm-encoder 0.231.0", + "wasmparser 0.231.0", "wat", ] [[package]] name = "wasm-encoder" version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" dependencies = [ "leb128fmt", "wasmparser 0.230.0", ] +[[package]] +name = "wasm-encoder" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +dependencies = [ + "leb128fmt", + "wasmparser 0.231.0", +] + [[package]] name = "wasm-metadata" version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52e010df5494f4289ccc68ce0c2a8c17555225a5e55cc41b98f5ea28d0844b" dependencies = [ "anyhow", "indexmap 2.7.0", - "wasm-encoder", + "wasm-encoder 0.230.0", "wasmparser 0.230.0", ] +[[package]] +name = "wasm-metadata" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "wasm-encoder 0.231.0", + "wasmparser 0.231.0", +] + [[package]] name = "wasm-mutate" -version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "egg", "log", "rand", "thiserror 1.0.65", - "wasm-encoder", - "wasmparser 0.230.0", + "wasm-encoder 0.231.0", + "wasmparser 0.231.0", ] [[package]] name = "wasm-smith" -version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "anyhow", "arbitrary", "flagset", "serde", "serde_derive", - "wasm-encoder", + "wasm-encoder 0.231.0", "wat", ] @@ -4153,13 +4175,13 @@ dependencies = [ [[package]] name = "wasm-wave" -version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "indexmap 2.7.0", "logos", "thiserror 1.0.65", - "wit-parser", + "wit-parser 0.231.0", ] [[package]] @@ -4219,7 +4241,19 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +dependencies = [ + "bitflags 2.6.0", + "hashbrown 0.15.2", + "indexmap 2.7.0", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -4230,12 +4264,12 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.230.0", + "wasmparser 0.231.0", ] [[package]] @@ -4283,9 +4317,9 @@ dependencies = [ "tempfile", "trait-variant", "wasi-common", - "wasm-encoder", + "wasm-encoder 0.231.0", "wasm-wave", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -4430,8 +4464,8 @@ dependencies = [ "trait-variant", "walkdir", "wasi-common", - "wasm-encoder", - "wasmparser 0.230.0", + "wasm-encoder 0.231.0", + "wasmparser 0.231.0", "wasmtime", "wasmtime-cache", "wasmtime-cli-flags", @@ -4449,10 +4483,10 @@ dependencies = [ "wasmtime-wasi-threads", "wasmtime-wasi-tls", "wasmtime-wast", - "wast 230.0.0", + "wast 231.0.0", "wat", "windows-sys 0.59.0", - "wit-component", + "wit-component 0.231.0", ] [[package]] @@ -4487,7 +4521,7 @@ dependencies = [ "wasmtime", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.231.0", ] [[package]] @@ -4513,7 +4547,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -4539,8 +4573,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder", - "wasmparser 0.230.0", + "wasm-encoder 0.231.0", + "wasmparser 0.231.0", "wasmprinter", "wasmtime-component-util", "wat", @@ -4553,7 +4587,7 @@ dependencies = [ "arbitrary", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmprinter", "wasmtime-environ", "wasmtime-test-util", @@ -4612,7 +4646,7 @@ dependencies = [ "rand", "smallvec", "target-lexicon", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime", "wasmtime-fuzzing", "wasmtime-test-util", @@ -4633,12 +4667,12 @@ dependencies = [ "target-lexicon", "tempfile", "v8", - "wasm-encoder", + "wasm-encoder 0.231.0", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmprinter", "wasmtime", "wasmtime-cli-flags", @@ -4867,7 +4901,7 @@ dependencies = [ "log", "tokio", "wasmtime", - "wast 230.0.0", + "wast 231.0.0", ] [[package]] @@ -4879,7 +4913,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -4892,7 +4926,7 @@ dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.7.0", - "wit-parser", + "wit-parser 0.231.0", ] [[package]] @@ -4910,22 +4944,22 @@ dependencies = [ [[package]] name = "wast" -version = "230.0.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "231.0.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder", + "wasm-encoder 0.231.0", ] [[package]] name = "wat" -version = "1.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +version = "1.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" dependencies = [ - "wast 230.0.0", + "wast 231.0.0", ] [[package]] @@ -5055,7 +5089,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.230.0", + "wasmparser 0.231.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -5226,7 +5260,7 @@ source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#5 dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser", + "wit-parser 0.230.0", ] [[package]] @@ -5267,9 +5301,9 @@ dependencies = [ "indexmap 2.7.0", "prettyplease", "syn 2.0.100", - "wasm-metadata", + "wasm-metadata 0.230.0", "wit-bindgen-core", - "wit-component", + "wit-component 0.230.0", ] [[package]] @@ -5289,7 +5323,8 @@ dependencies = [ [[package]] name = "wit-component" version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b607b15ead6d0e87f5d1613b4f18c04d4e80ceeada5ffa608d8360e6909881df" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -5298,16 +5333,35 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", + "wasm-encoder 0.230.0", + "wasm-metadata 0.230.0", "wasmparser 0.230.0", - "wit-parser", + "wit-parser 0.230.0", +] + +[[package]] +name = "wit-component" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +dependencies = [ + "anyhow", + "bitflags 2.6.0", + "indexmap 2.7.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.231.0", + "wasm-metadata 0.231.0", + "wasmparser 0.231.0", + "wit-parser 0.231.0", ] [[package]] name = "wit-parser" version = "0.230.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=new-async-abi#5c3a221ad50f6c77c6ed25726c397048d377cccb" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "679fde5556495f98079a8e6b9ef8c887f731addaffa3d48194075c1dd5cd611b" dependencies = [ "anyhow", "id-arena", @@ -5321,6 +5375,23 @@ dependencies = [ "wasmparser 0.230.0", ] +[[package]] +name = "wit-parser" +version = "0.231.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.7.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.231.0", +] + [[package]] name = "witx" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index bf3f77d666..2ce507733e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -300,17 +300,17 @@ wit-bindgen-rt = { version = "0.42.1", default-features = false } wit-bindgen-rust-macro = { version = "0.42.1", default-features = false } # wasm-tools family: -wasmparser = { version = "0.230.0", default-features = false, features = ['simd'] } -wat = "1.230.0" -wast = "230.0.0" -wasmprinter = "0.230.0" -wasm-encoder = "0.230.0" -wasm-smith = "0.230.0" -wasm-mutate = "0.230.0" -wit-parser = "0.230.0" -wit-component = "0.230.0" -wasm-wave = "0.230.0" -wasm-compose = "0.230.0" +wasmparser = { version = "0.231.0", default-features = false, features = ['simd'] } +wat = "1.231.0" +wast = "231.0.0" +wasmprinter = "0.231.0" +wasm-encoder = "0.231.0" +wasm-smith = "0.231.0" +wasm-mutate = "0.231.0" +wit-parser = "0.231.0" +wit-component = "0.231.0" +wasm-wave = "0.231.0" +wasm-compose = "0.231.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- @@ -589,18 +589,18 @@ lto = true # TODO: remove this once we've switched to a wasm-tools/wit-bindgen release: [patch.crates-io] -wasmparser = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wat = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wast = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasmprinter = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-encoder = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-smith = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-mutate = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wit-parser = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wit-component = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-wave = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-compose = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } -wasm-metadata = { git = "https://github.com/alexcrichton/wasm-tools", branch = 'new-async-abi' } +wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools" } +wat = { git = "https://github.com/bytecodealliance/wasm-tools" } +wast = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasmprinter = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-smith = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-mutate = { git = "https://github.com/bytecodealliance/wasm-tools" } +wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } +wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-wave = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools" } +wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" } wit-bindgen = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } wit-bindgen-rt = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } wit-bindgen-rust-macro = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } From cd7e18506fef976c8e6aa1928ed7764c90c0e6c5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2025 09:48:51 -0700 Subject: [PATCH 05/12] Review comments --- crates/environ/src/component/info.rs | 2 +- crates/environ/src/fact.rs | 4 ++-- crates/environ/src/fact/trampoline.rs | 9 +++++---- crates/wasmtime/src/runtime/component/concurrent.rs | 5 ++++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index acbd0f93dd..7a71a641aa 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -957,7 +957,7 @@ pub enum Trampoline { ResourceExitCall, /// An intrinsic used by FACT-generated modules to prepare a call involving - /// a sync/async-lowered import and sync/async-lifted export. + /// an async-lowered import and/or an async-lifted export. PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime matches the one specified in diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index d6e9aa1f56..62847cf9a4 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -790,8 +790,8 @@ 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 involving a - /// sync/async-lowered import and sync/async-lifted export. + /// An intrinsic used by FACT-generated modules to begin a call involving + /// an async-lowered import and/or an async-lifted export. PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime (if any) matches the one diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 7e35a376af..a284cbaa59 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -458,10 +458,11 @@ impl<'a, 'b> Compiler<'a, 'b> { /// Invokes the `prepare_call` builtin with the provided parameters for this /// adapter. /// - /// This is part of a {,a}sync -> {,a}sync adapter. This is done to create - /// the task on the host side of the runtime and such. This will notably - /// invoke a Cranelift builtin which will spill all wasm-level parameters to - /// the stack to handle variadic signatures. + /// This is part of a async lower and/or async lift adapter. This is not + /// used for a sync->sync function call. This is done to create the task on + /// the host side of the runtime and such. This will notably invoke a + /// Cranelift builtin which will spill all wasm-level parameters to the + /// stack to handle variadic signatures. /// /// Note that the `prepare_sync` parameter here configures the /// `result_count_or_max_if_async` parameter to indicate whether this is a diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index ba9ef17af1..ec48409427 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -3458,7 +3458,10 @@ impl Instance { /// pointers used for async calls). pub trait VMComponentAsyncStore { /// A helper function for fused adapter modules involving calls where the - /// caller is sync-lowered but the callee is async-lifted. + /// one of the caller or callee is async. + /// + /// This helper is not used when the caller and callee both use the sync + /// ABI, only when at least one is async is this used. unsafe fn prepare_call( &mut self, instance: &mut ComponentInstance, From 3565ed9958574a7da5171a42e3524d116fe79b80 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2025 15:54:23 -0700 Subject: [PATCH 06/12] Update dependencies, start taking into account wit-bindgen changes --- Cargo.lock | 215 +++++++----------- Cargo.toml | 52 ++--- .../component-async-tests/http/src/lib.rs | 14 +- .../wit/deps/http/types.wit | 2 +- .../src/bin/async_closed_streams.rs | 2 +- .../test-programs/src/bin/async_http_echo.rs | 6 +- .../src/bin/async_http_middleware.rs | 14 +- .../src/bin/async_transmit_callee.rs | 9 +- .../src/bin/async_transmit_caller.rs | 90 +++----- crates/test-programs/src/bin/p3_api_proxy.rs | 9 +- .../src/bin/p3_filesystem_file_read_write.rs | 4 +- ...p3_http_outbound_request_content_length.rs | 14 +- ...p3_http_outbound_request_invalid_header.rs | 2 +- ...outbound_request_missing_path_and_query.rs | 2 +- ...p3_http_outbound_request_response_build.rs | 20 +- .../bin/p3_sockets_tcp_sample_application.rs | 4 +- .../src/bin/p3_sockets_tcp_streams.rs | 6 +- crates/test-programs/src/p3/http.rs | 16 +- crates/wasi-http/Cargo.toml | 2 +- 19 files changed, 189 insertions(+), 294 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72b1ec2243..6f562d56b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -624,7 +624,7 @@ dependencies = [ "tokio", "wasi-http-draft", "wasm-compose", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime", "wasmtime-wasi", ] @@ -3480,7 +3480,7 @@ dependencies = [ "wasmtime", "wasmtime-test-util", "wat", - "wit-component 0.231.0", + "wit-component", ] [[package]] @@ -3897,7 +3897,7 @@ name = "verify-component-adapter" version = "34.0.0" dependencies = [ "anyhow", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wat", ] @@ -4017,7 +4017,7 @@ dependencies = [ "byte-array-literals", "object", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-encoder 0.231.0", + "wasm-encoder", "wit-bindgen-rust-macro", ] @@ -4078,8 +4078,9 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "0.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d529e2655cd5722a1d21754386461502bf0b3d7a36657b3f16554a416ce4d401" dependencies = [ "anyhow", "heck 0.4.1", @@ -4091,77 +4092,59 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.231.0", - "wasmparser 0.231.0", + "wasm-encoder", + "wasmparser 0.232.0", "wat", ] [[package]] name = "wasm-encoder" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" -dependencies = [ - "leb128fmt", - "wasmparser 0.230.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +checksum = "a447e61e38d1226b57e4628edadff36d16760be24a343712ba236b5106c95156" dependencies = [ "leb128fmt", - "wasmparser 0.231.0", + "wasmparser 0.232.0", ] [[package]] name = "wasm-metadata" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a52e010df5494f4289ccc68ce0c2a8c17555225a5e55cc41b98f5ea28d0844b" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "wasm-encoder 0.230.0", - "wasmparser 0.230.0", -] - -[[package]] -name = "wasm-metadata" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +checksum = "71aba7991cf9922a097b2dcc4c36ba5bc207339bdcd3a515530fc2555f01e8eb" dependencies = [ "anyhow", "indexmap 2.7.0", - "wasm-encoder 0.231.0", - "wasmparser 0.231.0", + "wasm-encoder", + "wasmparser 0.232.0", ] [[package]] name = "wasm-mutate" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "0.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bd4bdbd0388f94172ab752a07c43f790af003148127ca04840ca1bbc83f651" dependencies = [ "egg", "log", "rand", "thiserror 1.0.65", - "wasm-encoder 0.231.0", - "wasmparser 0.231.0", + "wasm-encoder", + "wasmparser 0.232.0", ] [[package]] name = "wasm-smith" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "0.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b3cbebe831a2319a70e6e971eb1aa92ab8f04e8c0b067040c0407e3882c688" dependencies = [ "anyhow", "arbitrary", "flagset", "serde", "serde_derive", - "wasm-encoder 0.231.0", + "wasm-encoder", "wat", ] @@ -4175,13 +4158,14 @@ dependencies = [ [[package]] name = "wasm-wave" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "0.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a66ccf53b27238b3e8a8a61db21edd500b637414b9948654f17f01b4ff2b82f" dependencies = [ "indexmap 2.7.0", "logos", "thiserror 1.0.65", - "wit-parser 0.231.0", + "wit-parser", ] [[package]] @@ -4240,20 +4224,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" -dependencies = [ - "bitflags 2.6.0", - "hashbrown 0.15.2", - "indexmap 2.7.0", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -4264,12 +4237,13 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "0.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be346255070ba58f65b24826bc94643dca065e797db7abb83e90965d086eba3" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.231.0", + "wasmparser 0.232.0", ] [[package]] @@ -4317,9 +4291,9 @@ dependencies = [ "tempfile", "trait-variant", "wasi-common", - "wasm-encoder 0.231.0", + "wasm-encoder", "wasm-wave", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -4464,8 +4438,8 @@ dependencies = [ "trait-variant", "walkdir", "wasi-common", - "wasm-encoder 0.231.0", - "wasmparser 0.231.0", + "wasm-encoder", + "wasmparser 0.232.0", "wasmtime", "wasmtime-cache", "wasmtime-cli-flags", @@ -4483,10 +4457,10 @@ dependencies = [ "wasmtime-wasi-threads", "wasmtime-wasi-tls", "wasmtime-wast", - "wast 231.0.0", + "wast 232.0.0", "wat", "windows-sys 0.59.0", - "wit-component 0.231.0", + "wit-component", ] [[package]] @@ -4521,7 +4495,7 @@ dependencies = [ "wasmtime", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.231.0", + "wit-parser", ] [[package]] @@ -4547,7 +4521,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -4573,8 +4547,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.231.0", - "wasmparser 0.231.0", + "wasm-encoder", + "wasmparser 0.232.0", "wasmprinter", "wasmtime-component-util", "wat", @@ -4587,7 +4561,7 @@ dependencies = [ "arbitrary", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmprinter", "wasmtime-environ", "wasmtime-test-util", @@ -4646,7 +4620,7 @@ dependencies = [ "rand", "smallvec", "target-lexicon", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime", "wasmtime-fuzzing", "wasmtime-test-util", @@ -4667,12 +4641,12 @@ dependencies = [ "target-lexicon", "tempfile", "v8", - "wasm-encoder 0.231.0", + "wasm-encoder", "wasm-mutate", "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmprinter", "wasmtime", "wasmtime-cli-flags", @@ -4901,7 +4875,7 @@ dependencies = [ "log", "tokio", "wasmtime", - "wast 231.0.0", + "wast 232.0.0", ] [[package]] @@ -4913,7 +4887,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -4926,7 +4900,7 @@ dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.7.0", - "wit-parser 0.231.0", + "wit-parser", ] [[package]] @@ -4944,22 +4918,24 @@ dependencies = [ [[package]] name = "wast" -version = "231.0.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "232.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5a22bfb0c309f5cf4b0cfa4fae77801e52570e88bff1344c1b7673a9977954" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.231.0", + "wasm-encoder", ] [[package]] name = "wat" -version = "1.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +version = "1.232.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2054d3da4289c8634a6ed0dd177e066518f45772c521b6959f5d6109f4c210" dependencies = [ - "wast 231.0.0", + "wast 232.0.0", ] [[package]] @@ -5089,7 +5065,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.231.0", + "wasmparser 0.232.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -5247,7 +5223,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.42.1" -source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "wit-bindgen-rt 0.42.1", "wit-bindgen-rust-macro", @@ -5256,11 +5232,11 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.42.1" -source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.230.0", + "wit-parser", ] [[package]] @@ -5284,7 +5260,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" version = "0.42.1" -source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "bitflags 2.6.0", "futures", @@ -5294,22 +5270,22 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.42.1" -source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "heck 0.5.0", "indexmap 2.7.0", "prettyplease", "syn 2.0.100", - "wasm-metadata 0.230.0", + "wasm-metadata", "wit-bindgen-core", - "wit-component 0.230.0", + "wit-component", ] [[package]] name = "wit-bindgen-rust-macro" version = "0.42.1" -source = "git+https://github.com/alexcrichton/wit-bindgen?branch=new-async-abi#59d48c64f8b7edf75c53b1fa3948b1ff0606f436" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "prettyplease", @@ -5322,27 +5298,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607b15ead6d0e87f5d1613b4f18c04d4e80ceeada5ffa608d8360e6909881df" -dependencies = [ - "anyhow", - "bitflags 2.6.0", - "indexmap 2.7.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder 0.230.0", - "wasm-metadata 0.230.0", - "wasmparser 0.230.0", - "wit-parser 0.230.0", -] - -[[package]] -name = "wit-component" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +checksum = "5d1d7a3444f5039b4461a66dc085d749a832e518a86e8c7498d6fdd9df776ed0" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -5351,34 +5309,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.231.0", - "wasm-metadata 0.231.0", - "wasmparser 0.231.0", - "wit-parser 0.231.0", + "wasm-encoder", + "wasm-metadata", + "wasmparser 0.232.0", + "wit-parser", ] [[package]] name = "wit-parser" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "679fde5556495f98079a8e6b9ef8c887f731addaffa3d48194075c1dd5cd611b" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.7.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.230.0", -] - -[[package]] -name = "wit-parser" -version = "0.231.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#14d084ccd698969aa8ff2479a32c08f306df10f3" +checksum = "00a0e031533e3f9082057b09b346f76d3af12a714feccbe8d32f926254319d86" dependencies = [ "anyhow", "id-arena", @@ -5389,7 +5330,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.231.0", + "wasmparser 0.232.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2ce507733e..d999962c17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -300,17 +300,17 @@ wit-bindgen-rt = { version = "0.42.1", default-features = false } wit-bindgen-rust-macro = { version = "0.42.1", default-features = false } # wasm-tools family: -wasmparser = { version = "0.231.0", default-features = false, features = ['simd'] } -wat = "1.231.0" -wast = "231.0.0" -wasmprinter = "0.231.0" -wasm-encoder = "0.231.0" -wasm-smith = "0.231.0" -wasm-mutate = "0.231.0" -wit-parser = "0.231.0" -wit-component = "0.231.0" -wasm-wave = "0.231.0" -wasm-compose = "0.231.0" +wasmparser = { version = "0.232.0", default-features = false, features = ['simd'] } +wat = "1.232.0" +wast = "232.0.0" +wasmprinter = "0.232.0" +wasm-encoder = "0.232.0" +wasm-smith = "0.232.0" +wasm-mutate = "0.232.0" +wit-parser = "0.232.0" +wit-component = "0.232.0" +wasm-wave = "0.232.0" +wasm-compose = "0.232.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- @@ -589,21 +589,21 @@ lto = true # TODO: remove this once we've switched to a wasm-tools/wit-bindgen release: [patch.crates-io] -wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools" } -wat = { git = "https://github.com/bytecodealliance/wasm-tools" } -wast = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasmprinter = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-smith = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-mutate = { git = "https://github.com/bytecodealliance/wasm-tools" } -wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } -wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-wave = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools" } -wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" } -wit-bindgen = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } -wit-bindgen-rt = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } -wit-bindgen-rust-macro = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'new-async-abi' } +# wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wat = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wast = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasmprinter = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-smith = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-mutate = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-wave = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools" } +# wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen" } +wit-bindgen-rt = { git = "https://github.com/bytecodealliance/wit-bindgen" } +wit-bindgen-rust-macro = { git = "https://github.com/bytecodealliance/wit-bindgen" } # wasmparser = { path = '../wasm-tools/crates/wasmparser' } # wat = { path = '../wasm-tools/crates/wat' } diff --git a/crates/misc/component-async-tests/http/src/lib.rs b/crates/misc/component-async-tests/http/src/lib.rs index 8ffefb2895..4c7f99c52e 100644 --- a/crates/misc/component-async-tests/http/src/lib.rs +++ b/crates/misc/component-async-tests/http/src/lib.rs @@ -249,19 +249,13 @@ impl wasi::http::types::HostBodyConcurrent for WasiHt async fn finish( accessor: &mut Accessor, this: Resource, - ) -> wasmtime::Result>> { - let trailers = accessor.with(|mut store| { + ) -> wasmtime::Result>>> { + let trailers = accessor.with(|mut store| -> wasmtime::Result<_> { let trailers = store.get().table().delete(this)?.trailers; - Ok::, anyhow::Error>(match trailers { - Some(t) => t, - None => { - let instance = store.instance(); - instance.future(&mut store)?.1 - } - }) + Ok(trailers) })?; - Ok(trailers.into()) + Ok(trailers.map(|t| t.into())) } } diff --git a/crates/misc/component-async-tests/wit/deps/http/types.wit b/crates/misc/component-async-tests/wit/deps/http/types.wit index 1b648878bc..08d4049769 100644 --- a/crates/misc/component-async-tests/wit/deps/http/types.wit +++ b/crates/misc/component-async-tests/wit/deps/http/types.wit @@ -266,7 +266,7 @@ interface types { /// Takes ownership of `body`, and returns a `trailers`. This function will /// trap if a `stream` child is still alive. - finish: static func(this: body) -> future; + finish: static func(this: body) -> option>; } /// Represents an HTTP Request. diff --git a/crates/test-programs/src/bin/async_closed_streams.rs b/crates/test-programs/src/bin/async_closed_streams.rs index 931a94ad5e..cf90cf4a96 100644 --- a/crates/test-programs/src/bin/async_closed_streams.rs +++ b/crates/test-programs/src/bin/async_closed_streams.rs @@ -24,7 +24,7 @@ impl Guest for Component { } async fn read_future(rx: FutureReader, expected: u8, _rx_ignored: FutureReader) { - assert_eq!(rx.await.unwrap(), expected); + assert_eq!(rx.await, expected); } } diff --git a/crates/test-programs/src/bin/async_http_echo.rs b/crates/test-programs/src/bin/async_http_echo.rs index d9f9a28d80..094ecb0031 100644 --- a/crates/test-programs/src/bin/async_http_echo.rs +++ b/crates/test-programs/src/bin/async_http_echo.rs @@ -30,7 +30,7 @@ impl Handler for Component { } 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 (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -51,8 +51,8 @@ impl Handler for Component { drop(pipe_tx); - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); diff --git a/crates/test-programs/src/bin/async_http_middleware.rs b/crates/test-programs/src/bin/async_http_middleware.rs index fde01550ca..c53ad49d5e 100644 --- a/crates/test-programs/src/bin/async_http_middleware.rs +++ b/crates/test-programs/src/bin/async_http_middleware.rs @@ -18,8 +18,8 @@ use { wit_future, wit_stream, }, flate2::{ - Compression, write::{DeflateDecoder, DeflateEncoder}, + Compression, }, std::{io::Write, mem}, wit_bindgen_rt::async_support::{self, StreamResult}, @@ -60,7 +60,7 @@ impl Handler for Component { 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 (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -84,8 +84,8 @@ impl Handler for Component { drop(pipe_tx); } - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); @@ -120,7 +120,7 @@ impl Handler for Component { // 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 (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -144,8 +144,8 @@ impl Handler for Component { drop(pipe_tx); } - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); diff --git a/crates/test-programs/src/bin/async_transmit_callee.rs b/crates/test-programs/src/bin/async_transmit_callee.rs index 9d884583db..533c3d5045 100644 --- a/crates/test-programs/src/bin/async_transmit_callee.rs +++ b/crates/test-programs/src/bin/async_transmit_callee.rs @@ -31,8 +31,8 @@ impl Guest for Component { 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(); + let (callee_future_tx1, callee_future_rx1) = wit_future::new(|| todo!()); + let (callee_future_tx2, callee_future_rx2) = wit_future::new(|| String::new()); async_support::spawn(async move { let mut caller_future_rx1 = Some(caller_future_rx1); @@ -50,10 +50,7 @@ impl Guest for Component { ); } Control::ReadFuture(value) => { - assert_eq!( - caller_future_rx1.take().unwrap().into_future().await, - Some(value) - ); + assert_eq!(caller_future_rx1.take().unwrap().into_future().await, value); } Control::WriteStream(value) => { assert!(callee_stream_tx.write_one(value).await.is_none()); diff --git a/crates/test-programs/src/bin/async_transmit_caller.rs b/crates/test-programs/src/bin/async_transmit_caller.rs index dfa03642e5..901025cb3c 100644 --- a/crates/test-programs/src/bin/async_transmit_caller.rs +++ b/crates/test-programs/src/bin/async_transmit_caller.rs @@ -14,10 +14,10 @@ use { local::local::transmit::{self, Control}, wit_future, wit_stream, }, - futures::{FutureExt, StreamExt, future, stream::FuturesUnordered}, + futures::{future, stream::FuturesUnordered, FutureExt, StreamExt}, std::{ future::{Future, IntoFuture}, - pin::{Pin, pin}, + pin::{pin, Pin}, task::Poll, }, wit_bindgen_rt::async_support::{FutureWriteCancel, StreamResult}, @@ -29,8 +29,8 @@ 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 caller_future_tx1, caller_future_rx1) = wit_future::new(|| todo!()); + let (caller_future_tx2, caller_future_rx2) = wit_future::new(|| String::new()); let (mut callee_stream_rx, mut callee_future_rx1, callee_future_rx2) = transmit::exchange( control_rx, @@ -41,12 +41,10 @@ impl Guest for Component { .await; // Tell peer to read from its end of the stream and assert that the result matches an expected value. - assert!( - control_tx - .write_one(Control::ReadStream("a".into())) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::ReadStream("a".into())) + .await + .is_none()); assert!(caller_stream_tx.write_one("a".into()).await.is_none()); // Start writing another value, but cancel the write before telling the peer to read. @@ -57,23 +55,19 @@ impl Guest for Component { // Tell the peer to read an expected value again, which should _not_ match the value provided in the // canceled write above. - assert!( - control_tx - .write_one(Control::ReadStream("c".into())) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::ReadStream("c".into())) + .await + .is_none()); assert!(caller_stream_tx.write_one("c".into()).await.is_none()); // Tell the peer to do a zero-length read, do a zero-length write; assert the latter completes, then do a // non-zero-length write, assert that it does _not_ complete, then tell the peer to do a non-zero-length // read and assert that the write completes. - assert!( - control_tx - .write_one(Control::ReadStreamZero) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::ReadStreamZero) + .await + .is_none()); { assert_eq!( caller_stream_tx.write(Vec::new()).await.0, @@ -116,22 +110,18 @@ impl Guest for Component { // Tell the peer to read an expected value again, which should _not_ match the value provided in the // canceled write above. - assert!( - control_tx - .write_one(Control::ReadFuture("y".into())) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::ReadFuture("y".into())) + .await + .is_none()); caller_future_tx1.write("y".into()).await.unwrap(); // Tell the peer to write a value to its end of the stream, then read from our end and assert the value // matches. - assert!( - control_tx - .write_one(Control::WriteStream("a".into())) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::WriteStream("a".into())) + .await + .is_none()); assert_eq!(callee_stream_rx.next().await, Some("a".into())); // Start reading a value from the stream, but cancel the read before telling the peer to write. @@ -142,22 +132,18 @@ impl Guest for Component { // 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. - assert!( - control_tx - .write_one(Control::WriteStream("b".into())) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::WriteStream("b".into())) + .await + .is_none()); assert_eq!(callee_stream_rx.next().await, Some("b".into())); // Tell the peer to do a zero-length write, assert that the read does _not_ complete, then tell the peer to // do a non-zero-length write and assert that the read completes. - assert!( - control_tx - .write_one(Control::WriteStreamZero) - .await - .is_none() - ); + assert!(control_tx + .write_one(Control::WriteStreamZero) + .await + .is_none()); { let next = Box::pin(callee_stream_rx.next()); let Err(next) = poll(next).await else { @@ -189,13 +175,11 @@ impl Guest for Component { // Tell the peer to write a value to its end of the future, then read from our end and assert the value // matches. - assert!( - control_tx - .write_one(Control::WriteFuture("b".into())) - .await - .is_none() - ); - assert_eq!(callee_future_rx1.into_future().await, Some("b".into())); + assert!(control_tx + .write_one(Control::WriteFuture("b".into())) + .await + .is_none()); + assert_eq!(callee_future_rx1.into_future().await, "b"); // Start writing a value to the stream, but drop the stream without telling the peer to read. let send = Box::pin(caller_stream_tx.write_one("d".into())); diff --git a/crates/test-programs/src/bin/p3_api_proxy.rs b/crates/test-programs/src/bin/p3_api_proxy.rs index 420b0e404a..8661e02a3f 100644 --- a/crates/test-programs/src/bin/p3_api_proxy.rs +++ b/crates/test-programs/src/bin/p3_api_proxy.rs @@ -34,7 +34,7 @@ impl test_programs::p3::proxy::exports::wasi::http::handler::Guest for T { let hdrs = Headers::new(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (resp, transmit) = Response::new(hdrs, Some(contents_rx), trailers_rx); spawn(async { join!( @@ -47,12 +47,7 @@ impl test_programs::p3::proxy::exports::wasi::http::handler::Guest for T { .await .expect("failed to write trailers"); }, - async { - transmit - .await - .expect("failed to transmit response") - .unwrap() - } + async { transmit.await.unwrap() } ); }); Ok(resp) diff --git a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs index 9023ec2bfe..94f1adeb8f 100644 --- a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs +++ b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs @@ -36,7 +36,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { ); let (data_rx, data_fut) = file.read_via_stream(0); let contents = data_rx.collect().await; - data_fut.await.unwrap().unwrap(); + data_fut.await.unwrap(); assert_eq!( String::from_utf8_lossy(&contents), "\0\0\0\0\0Hello, World!" @@ -45,7 +45,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { // Test that file read streams behave like other read streams. let (data_rx, data_fut) = file.read_via_stream(5); let contents = data_rx.collect().await; - data_fut.await.unwrap().unwrap(); + data_fut.await.unwrap(); assert_eq!(String::from_utf8_lossy(&contents), "Hello, World!"); dir.unlink_file_at(filename.to_string()).await.unwrap(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs b/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs index 6c2234aa7f..a8f8178eee 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs @@ -17,7 +17,7 @@ fn make_request() -> ( FutureReader>, ) { let (contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (request, transmit) = Request::new( Headers::from_list(&[("Content-Length".to_string(), b"11".to_vec())]).unwrap(), Some(contents_rx), @@ -67,9 +67,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - transmit - .expect("transmit sender dropped") - .expect("failed to transmit request"); + transmit.expect("failed to transmit request"); } { @@ -94,9 +92,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - let err = transmit - .expect("transmit sender dropped") - .expect_err("request transmission should have failed"); + let err = transmit.expect_err("request transmission should have failed"); assert!( matches!(err, ErrorCode::HttpRequestBodySize(Some(3))), "unexpected error: {err:#?}" @@ -122,9 +118,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - let err = transmit - .expect("transmit sender dropped") - .expect_err("request transmission should have failed"); + let err = transmit.expect_err("request transmission should have failed"); assert!( matches!(err, ErrorCode::HttpRequestBodySize(Some(18))), "unexpected error: {err:#?}" diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs b/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs index 10447d2fc8..2004d2edfe 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs @@ -63,7 +63,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { Err(HeaderError::InvalidSyntax) )); - let (_, rx) = wit_future::new(); + let (_, rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(hdrs, None, rx, None); let hdrs = req.headers(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs b/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs index 4d00350cd8..d56e3e393d 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs @@ -9,7 +9,7 @@ test_programs::p3::export!(Component); impl test_programs::p3::exports::wasi::cli::run::Guest for Component { async fn run() -> Result<(), ()> { let fields = Fields::new(); - let (_, rx) = wit_future::new(); + let (_, rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(fields, None, rx, None); req.set_method(&Method::Get).unwrap(); req.set_scheme(Some(&Scheme::Https)).unwrap(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs index 71598e7a09..102fc7ac92 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs @@ -15,7 +15,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { )]) .unwrap(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let (request, _) = Request::new(headers, Some(contents_rx), trailers_rx, None); request.set_method(&Method::Get).expect("setting method"); @@ -35,29 +35,27 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { )]) .unwrap(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let _ = Response::new(headers, Some(contents_rx), trailers_rx); let remaining = contents_tx.write_all(b"response-body".to_vec()).await; assert!(remaining.is_empty()); } { - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(Fields::new(), None, trailers_rx, None); - assert!( - req.set_method(&Method::Other("invalid method".to_string())) - .is_err() - ); + assert!(req + .set_method(&Method::Other("invalid method".to_string())) + .is_err()); assert!(req.set_authority(Some("bad-port:99999")).is_err()); assert!(req.set_authority(Some("bad-\nhost")).is_err()); assert!(req.set_authority(Some("too-many-ports:80:80:80")).is_err()); - assert!( - req.set_scheme(Some(&Scheme::Other("bad\nscheme".to_string()))) - .is_err() - ); + assert!(req + .set_scheme(Some(&Scheme::Other("bad\nscheme".to_string()))) + .is_err()); assert!(req.set_path_with_query(Some("/bad\npath")).is_err()); } diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs index b09eeb77b5..bc2d717d3d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs @@ -47,7 +47,7 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo // Check that we sent and received our message! assert_eq!(data, first_message); // Not guaranteed to work but should work in practice. - fut.await.unwrap().unwrap() + fut.await.unwrap() }, ); @@ -76,7 +76,7 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo // Check that we sent and received our message! assert_eq!(data, second_message); // Not guaranteed to work but should work in practice. - fut.await.unwrap().unwrap() + fut.await.unwrap() } ); } diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs index b2e100e575..6541f4a91d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs @@ -23,7 +23,7 @@ async fn test_tcp_input_stream_should_be_closed_by_remote_shutdown(family: IpAdd // Wait for the shutdown signal to reach the client: assert!(client_rx.next().await.is_none()); - assert_eq!(client_fut.await, Some(Ok(()))); + assert_eq!(client_fut.await, Ok(())); }) .await; } @@ -51,7 +51,7 @@ async fn test_tcp_input_stream_should_be_closed_by_local_shutdown(family: IpAddr // Shut down socket locally: drop(client_rx); // Wait for the shutdown signal to reach the client: - assert_eq!(client_fut.await, Some(Ok(()))); + assert_eq!(client_fut.await, Ok(())); }).await; } @@ -104,7 +104,7 @@ async fn test_tcp_shutdown_should_not_lose_data(family: IpAddressFamily) { outgoing_data, incoming_data, "Received data should match the sent data" ); - server_fut.await.unwrap().unwrap() + server_fut.await.unwrap() }, ); }) diff --git a/crates/test-programs/src/p3/http.rs b/crates/test-programs/src/p3/http.rs index fbbb707406..48ad71ce8e 100644 --- a/crates/test-programs/src/p3/http.rs +++ b/crates/test-programs/src/p3/http.rs @@ -1,4 +1,4 @@ -use anyhow::{Context as _, Result, anyhow}; +use anyhow::{anyhow, Context as _, Result}; use core::fmt; use futures::join; @@ -71,7 +71,7 @@ pub async fn request( .map_err(|_err| anyhow!("failed to set between_bytes_timeout"))?; let (mut contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| Ok(None)); let (request, transmit) = types::Request::new(headers, Some(contents_rx), trailers_rx, Some(options)); @@ -89,12 +89,7 @@ pub async fn request( .map_err(|()| anyhow!("failed to set path_with_query"))?; let (transmit, handle) = join!( - async { - transmit - .await - .context("transmit sender dropped")? - .context("failed to transmit request") - }, + async { transmit.await.context("failed to transmit request") }, async { let response = handler::handle(request).await?; let status = response.status_code(); @@ -112,10 +107,7 @@ pub async fn request( }, async { let body = body_rx.collect().await; - let trailers = trailers_rx - .await - .context("trailers sender dropped")? - .context("failed to read body")?; + let trailers = trailers_rx.await.context("failed to read body")?; let trailers = trailers.map(|trailers| trailers.entries()); anyhow::Ok(Response { status, diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index 3721b887a3..53cb3ab167 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -37,7 +37,7 @@ webpki-roots = { workspace = true } test-programs-artifacts = { workspace = true } test-log = { workspace = true } tracing-subscriber = { workspace = true } -wasmtime = { workspace = true, features = ['cranelift'] } +wasmtime = { workspace = true, features = ['cranelift', 'default'] } tokio = { workspace = true, features = ['macros'] } futures = { workspace = true, default-features = false, features = ['alloc'] } sha2 = "0.10.2" From 0d008aa467179e7cad56c192c4ab0271d1c708d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2025 16:02:18 -0700 Subject: [PATCH 07/12] rustfmt --- .../src/bin/async_http_middleware.rs | 2 +- .../src/bin/async_transmit_caller.rs | 84 +++++++++++-------- ...p3_http_outbound_request_response_build.rs | 14 ++-- crates/test-programs/src/p3/http.rs | 2 +- 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/crates/test-programs/src/bin/async_http_middleware.rs b/crates/test-programs/src/bin/async_http_middleware.rs index c53ad49d5e..17036d8af6 100644 --- a/crates/test-programs/src/bin/async_http_middleware.rs +++ b/crates/test-programs/src/bin/async_http_middleware.rs @@ -18,8 +18,8 @@ use { wit_future, wit_stream, }, flate2::{ - write::{DeflateDecoder, DeflateEncoder}, Compression, + write::{DeflateDecoder, DeflateEncoder}, }, std::{io::Write, mem}, wit_bindgen_rt::async_support::{self, StreamResult}, diff --git a/crates/test-programs/src/bin/async_transmit_caller.rs b/crates/test-programs/src/bin/async_transmit_caller.rs index 901025cb3c..596e4a7247 100644 --- a/crates/test-programs/src/bin/async_transmit_caller.rs +++ b/crates/test-programs/src/bin/async_transmit_caller.rs @@ -14,10 +14,10 @@ use { local::local::transmit::{self, Control}, wit_future, wit_stream, }, - futures::{future, stream::FuturesUnordered, FutureExt, StreamExt}, + futures::{FutureExt, StreamExt, future, stream::FuturesUnordered}, std::{ future::{Future, IntoFuture}, - pin::{pin, Pin}, + pin::{Pin, pin}, task::Poll, }, wit_bindgen_rt::async_support::{FutureWriteCancel, StreamResult}, @@ -41,10 +41,12 @@ impl Guest for Component { .await; // Tell peer to read from its end of the stream and assert that the result matches an expected value. - assert!(control_tx - .write_one(Control::ReadStream("a".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::ReadStream("a".into())) + .await + .is_none() + ); assert!(caller_stream_tx.write_one("a".into()).await.is_none()); // Start writing another value, but cancel the write before telling the peer to read. @@ -55,19 +57,23 @@ impl Guest for Component { // Tell the peer to read an expected value again, which should _not_ match the value provided in the // canceled write above. - assert!(control_tx - .write_one(Control::ReadStream("c".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::ReadStream("c".into())) + .await + .is_none() + ); assert!(caller_stream_tx.write_one("c".into()).await.is_none()); // Tell the peer to do a zero-length read, do a zero-length write; assert the latter completes, then do a // non-zero-length write, assert that it does _not_ complete, then tell the peer to do a non-zero-length // read and assert that the write completes. - assert!(control_tx - .write_one(Control::ReadStreamZero) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::ReadStreamZero) + .await + .is_none() + ); { assert_eq!( caller_stream_tx.write(Vec::new()).await.0, @@ -110,18 +116,22 @@ impl Guest for Component { // Tell the peer to read an expected value again, which should _not_ match the value provided in the // canceled write above. - assert!(control_tx - .write_one(Control::ReadFuture("y".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::ReadFuture("y".into())) + .await + .is_none() + ); caller_future_tx1.write("y".into()).await.unwrap(); // Tell the peer to write a value to its end of the stream, then read from our end and assert the value // matches. - assert!(control_tx - .write_one(Control::WriteStream("a".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::WriteStream("a".into())) + .await + .is_none() + ); assert_eq!(callee_stream_rx.next().await, Some("a".into())); // Start reading a value from the stream, but cancel the read before telling the peer to write. @@ -132,18 +142,22 @@ impl Guest for Component { // 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. - assert!(control_tx - .write_one(Control::WriteStream("b".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::WriteStream("b".into())) + .await + .is_none() + ); assert_eq!(callee_stream_rx.next().await, Some("b".into())); // Tell the peer to do a zero-length write, assert that the read does _not_ complete, then tell the peer to // do a non-zero-length write and assert that the read completes. - assert!(control_tx - .write_one(Control::WriteStreamZero) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::WriteStreamZero) + .await + .is_none() + ); { let next = Box::pin(callee_stream_rx.next()); let Err(next) = poll(next).await else { @@ -175,10 +189,12 @@ impl Guest for Component { // Tell the peer to write a value to its end of the future, then read from our end and assert the value // matches. - assert!(control_tx - .write_one(Control::WriteFuture("b".into())) - .await - .is_none()); + assert!( + control_tx + .write_one(Control::WriteFuture("b".into())) + .await + .is_none() + ); assert_eq!(callee_future_rx1.into_future().await, "b"); // Start writing a value to the stream, but drop the stream without telling the peer to read. diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs index 102fc7ac92..1667a8f9ba 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs @@ -45,17 +45,19 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { let (_, trailers_rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(Fields::new(), None, trailers_rx, None); - assert!(req - .set_method(&Method::Other("invalid method".to_string())) - .is_err()); + assert!( + req.set_method(&Method::Other("invalid method".to_string())) + .is_err() + ); assert!(req.set_authority(Some("bad-port:99999")).is_err()); assert!(req.set_authority(Some("bad-\nhost")).is_err()); assert!(req.set_authority(Some("too-many-ports:80:80:80")).is_err()); - assert!(req - .set_scheme(Some(&Scheme::Other("bad\nscheme".to_string()))) - .is_err()); + assert!( + req.set_scheme(Some(&Scheme::Other("bad\nscheme".to_string()))) + .is_err() + ); assert!(req.set_path_with_query(Some("/bad\npath")).is_err()); } diff --git a/crates/test-programs/src/p3/http.rs b/crates/test-programs/src/p3/http.rs index 48ad71ce8e..026889417a 100644 --- a/crates/test-programs/src/p3/http.rs +++ b/crates/test-programs/src/p3/http.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context as _, Result}; +use anyhow::{Context as _, Result, anyhow}; use core::fmt; use futures::join; From 90155aa066eac5c8fb856cbf3a1a570aadd90b21 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2025 16:22:31 -0700 Subject: [PATCH 08/12] Adjust wast file for new ABI changes --- .../component-model-async/partial-stream-copies.wast | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/misc_testsuite/component-model-async/partial-stream-copies.wast b/tests/misc_testsuite/component-model-async/partial-stream-copies.wast index a847c96241..c63d7921ff 100644 --- a/tests/misc_testsuite/component-model-async/partial-stream-copies.wast +++ b/tests/misc_testsuite/component-model-async/partial-stream-copies.wast @@ -153,7 +153,7 @@ (import "" "transform" (func $transform (param i32 i32) (result i32))) (func $run (export "run") (result i32) - (local $ret i32) (local $ret64 i64) (local $paramp i32) (local $retp i32) + (local $ret i32) (local $ret64 i64) (local $retp i32) (local $insr i32) (local $insw i32) (local $outsr i32) (local $subtask i32) (local $ws i32) @@ -168,10 +168,8 @@ (then unreachable)) ;; call 'transform' which will return a readable stream $outsr eagerly - (local.set $paramp (i32.const 4)) (local.set $retp (i32.const 8)) - (i32.store (local.get $paramp) (local.get $insr)) - (local.set $ret (call $transform (local.get $paramp) (local.get $retp))) + (local.set $ret (call $transform (local.get $insr) (local.get $retp))) (if (i32.ne (i32.const 2 (; RETURNED=2 | (0<<4) ;)) (local.get $ret)) (then unreachable)) (local.set $outsr (i32.load (local.get $retp))) @@ -193,7 +191,7 @@ (local.set $ret (call $stream.write (local.get $insw) (i32.const 16) (i32.const 12))) (if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret)) (then unreachable)) - + ;; wait for transform to read our write and close all the streams (local.set $ws (call $waitable-set.new)) (call $waitable.join (local.get $insw) (local.get $ws)) From f16368146c8df862a36ed64545f3f68c48d49138 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 27 May 2025 17:31:24 -0600 Subject: [PATCH 09/12] initial sketch of use-default-on-drop in `FutureWriter` Untested so far; will add tests in follow-up commit. Signed-off-by: Joel Dice --- .../tests/scenario/proxy.rs | 21 ++++---- .../tests/scenario/streams.rs | 32 ++++++------ .../tests/scenario/transmit.rs | 8 +-- crates/wasi-http/src/p3/host/types.rs | 18 +++---- crates/wasi/src/p3/filesystem/host.rs | 6 +-- crates/wasi/src/p3/mod.rs | 2 +- crates/wasi/src/p3/sockets/host/types/tcp.rs | 4 +- .../concurrent/futures_and_streams.rs | 52 ++++++++++++++----- 8 files changed, 82 insertions(+), 61 deletions(-) diff --git a/crates/misc/component-async-tests/tests/scenario/proxy.rs b/crates/misc/component-async-tests/tests/scenario/proxy.rs index 5bb56bf031..1b2d33ddc8 100644 --- a/crates/misc/component-async-tests/tests/scenario/proxy.rs +++ b/crates/misc/component-async-tests/tests/scenario/proxy.rs @@ -4,10 +4,10 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; use bytes::{Bytes, BytesMut}; -use component_async_tests::{Ctx, sleep}; +use component_async_tests::{sleep, Ctx}; use futures::{ - FutureExt, stream::{FuturesUnordered, TryStreamExt}, + FutureExt, }; use tokio::fs; use wasi_http_draft::wasi::http::types::{ErrorCode, Method, Scheme}; @@ -95,8 +95,8 @@ pub async fn async_http_middleware_with_chain() -> Result<()> { async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { use { flate2::{ - Compression, write::{DeflateDecoder, DeflateEncoder}, + Compression, }, std::io::Write, }; @@ -158,7 +158,8 @@ async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { let trailers = vec![("fizz".into(), b"buzz".into())]; - let (request_trailers_tx, request_trailers_rx) = instance.future(&mut store)?; + let (request_trailers_tx, request_trailers_rx) = + instance.future(|| unreachable!(), &mut store)?; let request_trailers = IoView::table(store.data_mut()).push(Fields(trailers.clone()))?; @@ -233,13 +234,11 @@ async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { (k.as_str(), v.as_slice()), ("content-encoding", b"deflate") ))); - assert!( - response - .headers - .0 - .iter() - .all(|(k, _)| k.as_str() != "content-length") - ); + assert!(response + .headers + .0 + .iter() + .all(|(k, _)| k.as_str() != "content-length")); } response_trailers = response.body.trailers.take(); diff --git a/crates/misc/component-async-tests/tests/scenario/streams.rs b/crates/misc/component-async-tests/tests/scenario/streams.rs index 567f6510db..27a60af8e9 100644 --- a/crates/misc/component-async-tests/tests/scenario/streams.rs +++ b/crates/misc/component-async-tests/tests/scenario/streams.rs @@ -1,6 +1,6 @@ use { anyhow::Result, - component_async_tests::{Ctx, closed_streams, util::config}, + component_async_tests::{closed_streams, util::config, Ctx}, futures::{ future::{self, FutureExt}, stream::{FuturesUnordered, StreamExt, TryStreamExt}, @@ -11,8 +11,8 @@ use { }, tokio::fs, wasmtime::{ - Engine, Store, component::{Component, Linker, ResourceTable, StreamReader, StreamWriter, VecBuffer}, + Engine, Store, }, wasmtime_wasi::p2::WasiCtxBuilder, }; @@ -65,24 +65,24 @@ pub async fn async_watch_streams() -> Result<()> { instance.run(&mut store, rx.watch_writer().0).await?; // Test watching and then dropping the read end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| unreachable!(), &mut store)?; let watch = tx.watch_reader().0; drop(rx); instance.run(&mut store, watch).await?; // Test dropping and then watching the read end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| unreachable!(), &mut store)?; drop(rx); instance.run(&mut store, tx.watch_reader().0).await?; // Test watching and then dropping the write end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; let watch = rx.watch_writer().0; drop(tx); instance.run(&mut store, watch).await?; // Test dropping and then watching the write end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; drop(tx); instance.run(&mut store, rx.watch_writer().0).await?; @@ -123,13 +123,11 @@ pub async fn async_watch_streams() -> Result<()> { instance .run(&mut store, future::poll_fn(|cx| future.as_mut().poll(cx))) .await?; - assert!( - instance - .run(&mut store, watch.into_inner().write_all(Some(42))) - .await? - .0 - .is_none() - ); + assert!(instance + .run(&mut store, watch.into_inner().write_all(Some(42))) + .await? + .0 + .is_none()); Ok(()) } @@ -242,8 +240,8 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->host { - let (tx, rx) = instance.future(&mut store)?; - let (tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(|| unreachable!(), &mut store)?; + let (tx_ignored, rx_ignored) = instance.future(|| unreachable!(), &mut store)?; let mut futures = FuturesUnordered::new(); futures.push(tx.write(value).map(FutureEvent::Write).boxed()); @@ -340,8 +338,8 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->guest { - let (tx, rx) = instance.future(&mut store)?; - let (tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(|| unreachable!(), &mut store)?; + let (tx_ignored, rx_ignored) = instance.future(|| unreachable!(), &mut store)?; let mut futures = FuturesUnordered::new(); futures.push( diff --git a/crates/misc/component-async-tests/tests/scenario/transmit.rs b/crates/misc/component-async-tests/tests/scenario/transmit.rs index 0e4c3b273b..56825d3f9a 100644 --- a/crates/misc/component-async-tests/tests/scenario/transmit.rs +++ b/crates/misc/component-async-tests/tests/scenario/transmit.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; use futures::{ future::{self, FutureExt}, stream::{FuturesUnordered, TryStreamExt}, @@ -17,7 +17,7 @@ use wasmtime_wasi::p2::WasiCtxBuilder; use component_async_tests::transmit::bindings::exports::local::local::transmit::Control; use component_async_tests::util::{compose, config, test_run, test_run_with_count}; -use component_async_tests::{Ctx, sleep, transmit}; +use component_async_tests::{sleep, transmit, Ctx}; use cancel::exports::local::local::cancel::Mode; @@ -345,8 +345,8 @@ async fn test_transmit_with(component: &[u8]) -> R let (control_tx, control_rx) = instance.stream::<_, _, Option<_>, _, _>(&mut store)?; let (caller_stream_tx, caller_stream_rx) = instance.stream::<_, _, Option<_>, _, _>(&mut store)?; - let (caller_future1_tx, caller_future1_rx) = instance.future(&mut store)?; - let (_caller_future2_tx, caller_future2_rx) = instance.future(&mut store)?; + let (caller_future1_tx, caller_future1_rx) = instance.future(|| unreachable!(), &mut store)?; + let (_caller_future2_tx, caller_future2_rx) = instance.future(|| unreachable!(), &mut store)?; let mut futures = FuturesUnordered::< Pin>> + Send + 'static>>, diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index cc06580883..bb1f4901af 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -9,13 +9,13 @@ use crate::p3::host::{ push_request, push_response, }; use crate::p3::{ - Body, BodyContext, BodyFrame, ContentLength, DEFAULT_BUFFER_CAPACITY, Request, RequestOptions, - Response, WasiHttp, WasiHttpImpl, WasiHttpView, + Body, BodyContext, BodyFrame, ContentLength, Request, RequestOptions, Response, WasiHttp, + WasiHttpImpl, WasiHttpView, DEFAULT_BUFFER_CAPACITY, }; -use anyhow::{Context as _, bail}; +use anyhow::{bail, Context as _}; use bytes::{Bytes, BytesMut}; -use core::future::Future; use core::future::poll_fn; +use core::future::Future; use core::mem; use core::ops::{Deref, DerefMut}; use core::pin::Pin; @@ -29,9 +29,9 @@ use std::sync::Arc; use wasmtime::component::{ Accessor, AccessorTask, FutureWriter, HostFuture, HostStream, Resource, StreamWriter, }; -use wasmtime_wasi::ResourceTable; use wasmtime_wasi::p3::bindings::clocks::monotonic_clock::Duration; use wasmtime_wasi::p3::{ResourceView as _, WithChildren}; +use wasmtime_wasi::ResourceTable; fn get_request_options<'a>( table: &'a ResourceTable, @@ -749,7 +749,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -793,7 +793,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Request { body, .. } = get_request_mut(binding.table(), &req)?; @@ -1075,7 +1075,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -1110,7 +1110,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Response { body, .. } = get_response_mut(binding.table(), &res)?; diff --git a/crates/wasi/src/p3/filesystem/host.rs b/crates/wasi/src/p3/filesystem/host.rs index 71538864c7..f23ce663e3 100644 --- a/crates/wasi/src/p3/filesystem/host.rs +++ b/crates/wasi/src/p3/filesystem/host.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use anyhow::{Context as _, anyhow}; +use anyhow::{anyhow, Context as _}; use system_interface::fs::FileIoExt as _; use tokio::sync::mpsc; use wasmtime::component::{ @@ -64,7 +64,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let fd = get_descriptor(binding.table(), &fd)?; @@ -321,7 +321,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let fd = get_descriptor(binding.table(), &fd)?; diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index 7ba7db3852..1bf86f20ea 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -152,7 +152,7 @@ where } } -pub struct IoTask { +pub struct IoTask { pub data: StreamWriter>, pub result: FutureWriter>, pub rx: mpsc::Receiver, E>>, diff --git a/crates/wasi/src/p3/sockets/host/types/tcp.rs b/crates/wasi/src/p3/sockets/host/types/tcp.rs index aa98a0a8ea..ade0c58c37 100644 --- a/crates/wasi/src/p3/sockets/host/types/tcp.rs +++ b/crates/wasi/src/p3/sockets/host/types/tcp.rs @@ -4,7 +4,7 @@ use core::net::SocketAddr; use std::net::Shutdown; use std::sync::Arc; -use anyhow::{Context as _, bail, ensure}; +use anyhow::{bail, ensure, Context as _}; use io_lifetimes::AsSocketlike as _; use rustix::io::Errno; use tokio::sync::mpsc; @@ -443,7 +443,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let sock = get_socket(binding.table(), &socket)?; diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index dab1e0c6a4..8fb391af1f 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1,21 +1,21 @@ use { super::{ + table::{TableDebug, TableId}, Event, GlobalErrorContextRefCount, HostTaskOutput, LocalErrorContextRefCount, StateTable, Waitable, WaitableCommon, WaitableState, - table::{TableDebug, TableId}, }, crate::{ - AsContextMut, StoreContextMut, VMStore, ValRaw, component::{ - Instance, Lower, Val, WasmList, WasmStr, func::{self, Lift, LiftContext, LowerContext, Options}, matching::InstanceType, values::{ErrorContextAny, FutureAny, StreamAny}, + Instance, Lower, Val, WasmList, WasmStr, }, store::{StoreId, StoreToken}, - vm::{SendSyncPtr, VMFuncRef, VMMemoryDefinition, component::ComponentInstance}, + vm::{component::ComponentInstance, SendSyncPtr, VMFuncRef, VMMemoryDefinition}, + AsContextMut, StoreContextMut, VMStore, ValRaw, }, - anyhow::{Context, Result, anyhow, bail}, + anyhow::{anyhow, bail, Context, Result}, buffers::Extender, futures::{ channel::{mpsc, oneshot}, @@ -202,10 +202,10 @@ fn accept_reader, U: 'static> tx: oneshot::Sender>, kind: TransmitKind, ) -> impl FnOnce(&mut dyn VMStore, &mut ComponentInstance, Reader) -> Result -+ Send -+ Sync -+ 'static -+ use { + + Send + + Sync + + 'static + + use { let token = StoreToken::new(store); move |store, instance, reader| { let code = match reader { @@ -381,7 +381,7 @@ enum WriteEvent { tx: oneshot::Sender>, }, /// Close the write end of the stream or future. - Close, + Close(Option B + Send + Sync>>), /// Watch the read (i.e. opposite) end of this stream or future, dropping /// the specified sender when it is closed. Watch { tx: oneshot::Sender<()> }, @@ -488,17 +488,20 @@ fn watch( } /// Represents the writable end of a Component Model `future`. -pub struct FutureWriter { +pub struct FutureWriter { + default: Option T>, instance: SendSyncPtr, tx: Option>>>, } impl FutureWriter { fn new( + default: fn() -> T, tx: Option>>>, instance: &mut ComponentInstance, ) -> Self { Self { + default: Some(default), instance: SendSyncPtr::new(instance.into()), tx, } @@ -525,6 +528,7 @@ impl FutureWriter { tx, }, ); + self.default = None; let instance = self.instance; super::checked( instance, @@ -563,7 +567,13 @@ impl FutureWriter { impl Drop for FutureWriter { fn drop(&mut self) { if let Some(mut tx) = self.tx.take() { - send(&mut tx, WriteEvent::Close); + send( + &mut tx, + WriteEvent::Close(self.default.take().map(|v| { + Box::new(move || Some(v())) + as Box Option + Send + Sync + 'static> + })), + ); } } } @@ -960,7 +970,7 @@ impl StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { if let Some(mut tx) = self.tx.take() { - send(&mut tx, WriteEvent::Close); + send(&mut tx, WriteEvent::Close(None)); } } } @@ -1523,6 +1533,7 @@ impl Instance { S: AsContextMut, >( &self, + default: fn() -> T, mut store: S, ) -> Result<(FutureWriter, FutureReader)> { store @@ -1532,6 +1543,7 @@ impl Instance { Ok(( FutureWriter::new( + default, Some(instance.start_write_event_loop::<_, _, U>( store.as_context_mut(), write.rep(), @@ -1674,7 +1686,19 @@ impl ComponentInstance { ) })? } - WriteEvent::Close => super::with_local_instance(|_, instance| { + WriteEvent::Close(default) => super::with_local_instance(|_, instance| { + if let Some(default) = default { + super::with_local_instance(|store, instance| { + instance.host_write::<_, _, U>( + token.as_context_mut(store), + rep, + default(), + PostWrite::Continue, + oneshot::channel().0, + kind, + ) + })?; + } instance.host_close_writer(rep, kind) })?, WriteEvent::Watch { tx } => super::with_local_instance(|_, instance| { From c6e04f58b2fbb11d581f2bdc73c138b47ad4f5d2 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 28 May 2025 08:26:59 -0600 Subject: [PATCH 10/12] fix test regressions Signed-off-by: Joel Dice --- .../tests/scenario/streams.rs | 6 ++-- crates/wasi-http/src/p3/host/types.rs | 8 ++--- .../concurrent/futures_and_streams.rs | 29 +++++++++++++------ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/crates/misc/component-async-tests/tests/scenario/streams.rs b/crates/misc/component-async-tests/tests/scenario/streams.rs index 27a60af8e9..4d1d653c74 100644 --- a/crates/misc/component-async-tests/tests/scenario/streams.rs +++ b/crates/misc/component-async-tests/tests/scenario/streams.rs @@ -65,13 +65,13 @@ pub async fn async_watch_streams() -> Result<()> { instance.run(&mut store, rx.watch_writer().0).await?; // Test watching and then dropping the read end of a future. - let (tx, rx) = instance.future::(|| unreachable!(), &mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; let watch = tx.watch_reader().0; drop(rx); instance.run(&mut store, watch).await?; // Test dropping and then watching the read end of a future. - let (tx, rx) = instance.future::(|| unreachable!(), &mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; drop(rx); instance.run(&mut store, tx.watch_reader().0).await?; @@ -241,7 +241,7 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->host { let (tx, rx) = instance.future(|| unreachable!(), &mut store)?; - let (tx_ignored, rx_ignored) = instance.future(|| unreachable!(), &mut store)?; + let (tx_ignored, rx_ignored) = instance.future(|| 42u8, &mut store)?; let mut futures = FuturesUnordered::new(); futures.push(tx.write(value).map(FutureEvent::Write).boxed()); diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index bb1f4901af..fa40563898 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -749,7 +749,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(|| unreachable!(), &mut view) + .future(|| Ok(()), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -793,7 +793,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(|| unreachable!(), &mut view) + .future(|| Ok(None), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Request { body, .. } = get_request_mut(binding.table(), &req)?; @@ -1075,7 +1075,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(|| unreachable!(), &mut view) + .future(|| Ok(()), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -1110,7 +1110,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(|| unreachable!(), &mut view) + .future(|| Ok(None), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Response { body, .. } = get_response_mut(binding.table(), &res)?; diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index 8fb391af1f..ec286b1c83 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1686,9 +1686,9 @@ impl ComponentInstance { ) })? } - WriteEvent::Close(default) => super::with_local_instance(|_, instance| { - if let Some(default) = default { - super::with_local_instance(|store, instance| { + WriteEvent::Close(default) => { + super::with_local_instance(|store, instance| { + if let Some(default) = default { instance.host_write::<_, _, U>( token.as_context_mut(store), rep, @@ -1696,11 +1696,11 @@ impl ComponentInstance { PostWrite::Continue, oneshot::channel().0, kind, - ) - })?; - } - instance.host_close_writer(rep, kind) - })?, + )?; + } + instance.host_close_writer(rep, kind) + })? + } WriteEvent::Watch { tx } => super::with_local_instance(|_, instance| { let state = instance.get_mut(TableId::::new(rep))?; if !matches!(&state.read, ReadState::Closed) { @@ -1764,7 +1764,18 @@ impl ComponentInstance { })?, ReadEvent::Watch { tx } => super::with_local_instance(|_, instance| { let state = instance.get_mut(TableId::::new(rep))?; - if !matches!(&state.write, WriteState::Closed) { + if !matches!( + &state.write, + WriteState::Closed + | WriteState::GuestReady { + post_write: PostWrite::Close, + .. + } + | WriteState::HostReady { + post_write: PostWrite::Close, + .. + } + ) { state.writer_watcher = Some(tx); } Ok::<_, anyhow::Error>(()) From 5f081d311ce218dfb1228b28f403d0412b47272f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 28 May 2025 07:38:01 -0700 Subject: [PATCH 11/12] Run rustfmt --- .../tests/scenario/proxy.rs | 18 ++++++++++-------- .../tests/scenario/streams.rs | 16 +++++++++------- .../tests/scenario/transmit.rs | 4 ++-- crates/wasi-http/src/p3/host/types.rs | 10 +++++----- crates/wasi/src/p3/filesystem/host.rs | 2 +- crates/wasi/src/p3/sockets/host/types/tcp.rs | 2 +- .../concurrent/futures_and_streams.rs | 18 +++++++++--------- 7 files changed, 37 insertions(+), 33 deletions(-) diff --git a/crates/misc/component-async-tests/tests/scenario/proxy.rs b/crates/misc/component-async-tests/tests/scenario/proxy.rs index 1b2d33ddc8..cb4a0b3c2a 100644 --- a/crates/misc/component-async-tests/tests/scenario/proxy.rs +++ b/crates/misc/component-async-tests/tests/scenario/proxy.rs @@ -4,10 +4,10 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; use bytes::{Bytes, BytesMut}; -use component_async_tests::{sleep, Ctx}; +use component_async_tests::{Ctx, sleep}; use futures::{ - stream::{FuturesUnordered, TryStreamExt}, FutureExt, + stream::{FuturesUnordered, TryStreamExt}, }; use tokio::fs; use wasi_http_draft::wasi::http::types::{ErrorCode, Method, Scheme}; @@ -95,8 +95,8 @@ pub async fn async_http_middleware_with_chain() -> Result<()> { async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { use { flate2::{ - write::{DeflateDecoder, DeflateEncoder}, Compression, + write::{DeflateDecoder, DeflateEncoder}, }, std::io::Write, }; @@ -234,11 +234,13 @@ async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { (k.as_str(), v.as_slice()), ("content-encoding", b"deflate") ))); - assert!(response - .headers - .0 - .iter() - .all(|(k, _)| k.as_str() != "content-length")); + assert!( + response + .headers + .0 + .iter() + .all(|(k, _)| k.as_str() != "content-length") + ); } response_trailers = response.body.trailers.take(); diff --git a/crates/misc/component-async-tests/tests/scenario/streams.rs b/crates/misc/component-async-tests/tests/scenario/streams.rs index 4d1d653c74..b98454a536 100644 --- a/crates/misc/component-async-tests/tests/scenario/streams.rs +++ b/crates/misc/component-async-tests/tests/scenario/streams.rs @@ -1,6 +1,6 @@ use { anyhow::Result, - component_async_tests::{closed_streams, util::config, Ctx}, + component_async_tests::{Ctx, closed_streams, util::config}, futures::{ future::{self, FutureExt}, stream::{FuturesUnordered, StreamExt, TryStreamExt}, @@ -11,8 +11,8 @@ use { }, tokio::fs, wasmtime::{ - component::{Component, Linker, ResourceTable, StreamReader, StreamWriter, VecBuffer}, Engine, Store, + component::{Component, Linker, ResourceTable, StreamReader, StreamWriter, VecBuffer}, }, wasmtime_wasi::p2::WasiCtxBuilder, }; @@ -123,11 +123,13 @@ pub async fn async_watch_streams() -> Result<()> { instance .run(&mut store, future::poll_fn(|cx| future.as_mut().poll(cx))) .await?; - assert!(instance - .run(&mut store, watch.into_inner().write_all(Some(42))) - .await? - .0 - .is_none()); + assert!( + instance + .run(&mut store, watch.into_inner().write_all(Some(42))) + .await? + .0 + .is_none() + ); Ok(()) } diff --git a/crates/misc/component-async-tests/tests/scenario/transmit.rs b/crates/misc/component-async-tests/tests/scenario/transmit.rs index 56825d3f9a..41794d5aa0 100644 --- a/crates/misc/component-async-tests/tests/scenario/transmit.rs +++ b/crates/misc/component-async-tests/tests/scenario/transmit.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use futures::{ future::{self, FutureExt}, stream::{FuturesUnordered, TryStreamExt}, @@ -17,7 +17,7 @@ use wasmtime_wasi::p2::WasiCtxBuilder; use component_async_tests::transmit::bindings::exports::local::local::transmit::Control; use component_async_tests::util::{compose, config, test_run, test_run_with_count}; -use component_async_tests::{sleep, transmit, Ctx}; +use component_async_tests::{Ctx, sleep, transmit}; use cancel::exports::local::local::cancel::Mode; diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index fa40563898..c644d993ae 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -9,13 +9,13 @@ use crate::p3::host::{ push_request, push_response, }; use crate::p3::{ - Body, BodyContext, BodyFrame, ContentLength, Request, RequestOptions, Response, WasiHttp, - WasiHttpImpl, WasiHttpView, DEFAULT_BUFFER_CAPACITY, + Body, BodyContext, BodyFrame, ContentLength, DEFAULT_BUFFER_CAPACITY, Request, RequestOptions, + Response, WasiHttp, WasiHttpImpl, WasiHttpView, }; -use anyhow::{bail, Context as _}; +use anyhow::{Context as _, bail}; use bytes::{Bytes, BytesMut}; -use core::future::poll_fn; use core::future::Future; +use core::future::poll_fn; use core::mem; use core::ops::{Deref, DerefMut}; use core::pin::Pin; @@ -29,9 +29,9 @@ use std::sync::Arc; use wasmtime::component::{ Accessor, AccessorTask, FutureWriter, HostFuture, HostStream, Resource, StreamWriter, }; +use wasmtime_wasi::ResourceTable; use wasmtime_wasi::p3::bindings::clocks::monotonic_clock::Duration; use wasmtime_wasi::p3::{ResourceView as _, WithChildren}; -use wasmtime_wasi::ResourceTable; fn get_request_options<'a>( table: &'a ResourceTable, diff --git a/crates/wasi/src/p3/filesystem/host.rs b/crates/wasi/src/p3/filesystem/host.rs index f23ce663e3..5798ce012a 100644 --- a/crates/wasi/src/p3/filesystem/host.rs +++ b/crates/wasi/src/p3/filesystem/host.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use anyhow::{anyhow, Context as _}; +use anyhow::{Context as _, anyhow}; use system_interface::fs::FileIoExt as _; use tokio::sync::mpsc; use wasmtime::component::{ diff --git a/crates/wasi/src/p3/sockets/host/types/tcp.rs b/crates/wasi/src/p3/sockets/host/types/tcp.rs index ade0c58c37..aa7dabfcd4 100644 --- a/crates/wasi/src/p3/sockets/host/types/tcp.rs +++ b/crates/wasi/src/p3/sockets/host/types/tcp.rs @@ -4,7 +4,7 @@ use core::net::SocketAddr; use std::net::Shutdown; use std::sync::Arc; -use anyhow::{bail, ensure, Context as _}; +use anyhow::{Context as _, bail, ensure}; use io_lifetimes::AsSocketlike as _; use rustix::io::Errno; use tokio::sync::mpsc; diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index ec286b1c83..657604262d 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1,21 +1,21 @@ use { super::{ - table::{TableDebug, TableId}, Event, GlobalErrorContextRefCount, HostTaskOutput, LocalErrorContextRefCount, StateTable, Waitable, WaitableCommon, WaitableState, + table::{TableDebug, TableId}, }, crate::{ + AsContextMut, StoreContextMut, VMStore, ValRaw, component::{ + Instance, Lower, Val, WasmList, WasmStr, func::{self, Lift, LiftContext, LowerContext, Options}, matching::InstanceType, values::{ErrorContextAny, FutureAny, StreamAny}, - Instance, Lower, Val, WasmList, WasmStr, }, store::{StoreId, StoreToken}, - vm::{component::ComponentInstance, SendSyncPtr, VMFuncRef, VMMemoryDefinition}, - AsContextMut, StoreContextMut, VMStore, ValRaw, + vm::{SendSyncPtr, VMFuncRef, VMMemoryDefinition, component::ComponentInstance}, }, - anyhow::{anyhow, bail, Context, Result}, + anyhow::{Context, Result, anyhow, bail}, buffers::Extender, futures::{ channel::{mpsc, oneshot}, @@ -202,10 +202,10 @@ fn accept_reader, U: 'static> tx: oneshot::Sender>, kind: TransmitKind, ) -> impl FnOnce(&mut dyn VMStore, &mut ComponentInstance, Reader) -> Result - + Send - + Sync - + 'static - + use { ++ Send ++ Sync ++ 'static ++ use { let token = StoreToken::new(store); move |store, instance, reader| { let code = match reader { From d06892f2a56957ac81bb5822819dc9fba85b5e31 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 28 May 2025 08:44:29 -0600 Subject: [PATCH 12/12] add docs for `default` parameter of `Instance::future` Signed-off-by: Joel Dice --- .../component/concurrent/futures_and_streams.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index 657604262d..78f3deb553 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -1527,6 +1527,17 @@ enum Reader<'a> { impl Instance { /// Create a new Component Model `future` as pair of writable and readable ends, /// the latter of which may be passed to guest code. + /// + /// The `default` parameter will be used if the returned `FutureWriter` is + /// dropped before `FutureWriter::write` is called. Since the write end of + /// a Component Model `future` must be written to before it is closed, and + /// since Rust does not currently provide a way to statically enforce that + /// (e.g. linear typing), we use this mechanism to ensure a value is always + /// written prior to closing. + /// + /// If there's no plausible default value, and you're sure + /// `FutureWriter::write` will be called, you can consider passing `|| + /// unreachable!()` as the `default` parameter. pub fn future< T: func::Lower + func::Lift + Send + Sync + 'static, U: 'static,