diff --git a/Cargo.lock b/Cargo.lock index 9bf4a6328..2c2a36d7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,9 @@ name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +dependencies = [ + "backtrace", +] [[package]] name = "arbitrary" @@ -1436,7 +1439,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wit-bindgen-core", "wit-component", "wit-parser 0.214.0", @@ -1717,6 +1720,14 @@ name = "wasm-encoder" version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff694f02a8d7a50b6922b197ae03883fbf18cdb2ae9fbee7b6148456f5f44041" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.214.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" dependencies = [ "leb128", "wasmparser 0.214.0", @@ -1725,8 +1736,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865c5bff5f7a3781b5f92ea4cfa99bb38267da097441cdb09080de1568ef3075" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" dependencies = [ "anyhow", "indexmap", @@ -1734,7 +1744,7 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wasmparser 0.214.0", ] @@ -1755,8 +1765,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" dependencies = [ "ahash", "bitflags", @@ -1828,7 +1837,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat", + "wat 1.214.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2079,7 +2088,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "214.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", ] [[package]] @@ -2088,7 +2109,15 @@ version = "1.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "347249eb56773fa728df2656cfe3a8c19437ded61a922a0b5e0839d9790e278e" dependencies = [ - "wast 214.0.0", + "wast 214.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.214.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +dependencies = [ + "wast 214.0.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", ] [[package]] @@ -2355,7 +2384,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2370,7 +2399,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wasmparser 0.214.0", "wasmtime", "wasmtime-wasi", @@ -2403,7 +2432,7 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wasm-metadata", "wasmparser 0.214.0", "wit-bindgen-core", @@ -2490,8 +2519,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9fd46f0e783bf80f1ab7291f9d442fa5553ff0e96cdb71964bd8859b734b55" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" dependencies = [ "anyhow", "bitflags", @@ -2500,10 +2528,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.214.0", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wasm-metadata", "wasmparser 0.214.0", - "wat", + "wat 1.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", "wit-parser 0.214.0", ] @@ -2528,8 +2556,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681d526d6ea42e28f9afe9eae2b50e0b0a627aef8822c75eb04078db84d03e57" +source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 87abd95f5..76cdedb47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,11 @@ indexmap = "2.0.0" prettyplease = "0.2.20" syn = { version = "2.0", features = ["printing"] } -wasmparser = "0.214.0" -wasm-encoder = "0.214.0" -wasm-metadata = "0.214.0" -wit-parser = "0.214.0" -wit-component = "0.214.0" +wasmparser = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } +wasm-encoder = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } +wasm-metadata = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } +wit-parser = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } +wit-component = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } wit-bindgen-core = { path = 'crates/core', version = '0.28.0' } wit-bindgen-c = { path = 'crates/c', version = '0.28.0' } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 99764e1c6..7c5f78c01 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -717,6 +717,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool { | TypeDefKind::Result(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) + | TypeDefKind::Error | TypeDefKind::Unknown => false, } } @@ -747,6 +748,7 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { | TypeDefKind::Resource | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) + | TypeDefKind::Error | TypeDefKind::Variant(_) => { unimplemented!() } @@ -993,6 +995,7 @@ impl Return { TypeDefKind::Stream(_) => todo!("return_single for stream"), TypeDefKind::Resource => todo!("return_single for resource"), TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } self.retptrs.push(*orig_ty); @@ -1426,13 +1429,17 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for future"); } - fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Stream, _docs: &Docs) { + fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { todo!("print_anonymous_type for stream"); } fn anonymous_typ_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { todo!("print_anonymous_type for typ"); } + + fn anonymous_type_error(&mut self, _id: TypeId, _docs: &Docs) { + todo!() + } } pub enum CTypeNameInfo<'a> { @@ -1609,6 +1616,7 @@ impl InterfaceGenerator<'_> { self.free(&Type::Id(*id), "*ptr"); } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } if c_helpers_body_start == self.src.c_helpers.len() { self.src.c_helpers.as_mut_string().truncate(c_helpers_start); @@ -1749,6 +1757,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut f, + false, ); let FunctionBindgen { @@ -1821,6 +1830,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut f, + false, ); let FunctionBindgen { src, .. } = f; self.src.c_adapters(&src); @@ -1851,7 +1861,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f); + abi::post_return(f.gen.resolve, func, &mut f, false); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -2078,18 +2088,12 @@ impl InterfaceGenerator<'_> { .as_ref() .map_or(false, |ty| self.contains_droppable_borrow(ty)), - TypeDefKind::Stream(s) => { - s.element - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - || s.end - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - } + TypeDefKind::Stream(s) => self.contains_droppable_borrow(s), TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty), TypeDefKind::Unknown => false, + TypeDefKind::Error => todo!(), } } else { false @@ -2752,7 +2756,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src.push_str(");\n"); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { @@ -3146,6 +3150,7 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"), TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), }, Type::String => true, _ => false, diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index d696b459a..e3eab743e 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -350,6 +350,40 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Create an `i32` from a future. + FutureLower { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create a future from an `i32`. + FutureLift { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from a stream. + StreamLower { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create a stream from an `i32`. + StreamLift { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from an error. + ErrorLower { + ty: TypeId, + } : [1] => [1], + + /// Create a error from an `i32`. + ErrorLift { + ty: TypeId, + } : [1] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of /// its fields, and then pushes the fields onto the stack. TupleLower { @@ -470,7 +504,8 @@ def_instruction! { /// Note that this will be used for async functions. CallInterface { func: &'a Function, - } : [func.params.len()] => [func.results.len()], + async_: bool, + } : [func.params.len()] => [if *async_ { 1 } else { func.results.len() }], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -519,6 +554,22 @@ def_instruction! { GuestDeallocateVariant { blocks: usize, } : [1] => [0], + + AsyncMalloc { size: usize, align: usize } : [0] => [1], + + AsyncCallWasm { name: &'a str, size: usize, align: usize } : [3] => [0], + + AsyncCallStart { + name: &'a str, + params: &'a [WasmType], + results: &'a [WasmType] + } : [params.len()] => [results.len()], + + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], + + AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], + + Flush { amt: usize } : [*amt] => [*amt], } } @@ -683,8 +734,9 @@ pub fn call( lift_lower: LiftLower, func: &Function, bindgen: &mut impl Bindgen, + async_: bool, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func); } /// Used in a similar manner as the `Interface::call` function except is @@ -693,12 +745,13 @@ pub fn call( /// This is only intended to be used in guest generators for exported /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. -pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { +pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen, async_: bool) { Generator::new( resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, bindgen, + async_, ) .post_return(func); } @@ -734,7 +787,9 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { .filter_map(|t| t.as_ref()) .any(|t| needs_post_return(resolve, t)), TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => { + unimplemented!() + } TypeDefKind::Unknown => unreachable!(), }, @@ -757,6 +812,7 @@ struct Generator<'a, B: Bindgen> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, resolve: &'a Resolve, operands: Vec, results: Vec, @@ -770,12 +826,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, ) -> Generator<'a, B> { Generator { resolve, variant, lift_lower, bindgen, + async_, operands: Vec::new(), results: Vec::new(), stack: Vec::new(), @@ -784,70 +842,115 @@ impl<'a, B: Bindgen> Generator<'a, B> { } fn call(&mut self, func: &Function) { + const MAX_FLAT_PARAMS: usize = 16; + const MAX_FLAT_RESULTS: usize = 1; + let sig = self.resolve.wasm_signature(self.variant, func); match self.lift_lower { LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. + if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { + let mut offset = 0usize; for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); + self_.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self_.bindgen.sizes().align(ty)); + self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty); } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. + + self_.stack.push(ptr); + }; + + let params_size_align = if self.async_ { let (size, align) = self .bindgen .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size, - align, - }); - self.stack.pop().unwrap() + .record(func.params.iter().map(|(_, ty)| ty)); + self.emit(&Instruction::AsyncMalloc { size, align }); + let ptr = self.stack.pop().unwrap(); + lower_to_memory(self, ptr); + Some((size, align)) + } else { + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size, + align, + }); + self.stack.pop().unwrap() + } + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } + }; + lower_to_memory(self, ptr); } - - self.stack.push(ptr); - } + None + }; // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); + let dealloc_size_align = if let Some((params_size, params_align)) = + params_size_align + { + let (size, align) = self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { size, align }); + let ptr = self.stack.pop().unwrap(); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); - } + // ... and another return pointer for the call handle + self.stack.push(self.bindgen.return_pointer(4, 4)); + + assert_eq!(self.stack.len(), 3); + self.emit(&Instruction::AsyncCallWasm { + name: &format!("[async]{}", func.name), + size: params_size, + align: params_align, + }); + Some((size, align)) + } else { + if self.variant == AbiVariant::GuestImport && sig.retptr { + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + None + }; - if !sig.retptr { + if !(sig.retptr || self.async_) { // With no return pointer in use we can simply lift the // result(s) of the function from the result of the core // wasm function. @@ -858,7 +961,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { let ptr = match self.variant { // imports into guests means it's a wasm module // calling an imported function. We supplied the - // return poitner as the last argument (saved in + // return pointer as the last argument (saved in // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { @@ -870,9 +973,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { // calling wasm so wasm returned a pointer to where // the result is stored AbiVariant::GuestExport => self.stack.pop().unwrap(), + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.emit(&Instruction::Flush { + amt: func.results.len(), + }); + + if let Some((size, align)) = dealloc_size_align { + self.stack.push(ptr); + self.emit(&Instruction::GuestDeallocate { size, align }); + } } self.emit(&Instruction::Return { @@ -881,7 +996,53 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } LiftLower::LiftArgsLowerResults => { - if !sig.indirect_params { + if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let read_from_memory = |self_: &mut Self| { + let mut offset = 0usize; + let ptr = self_.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self_.bindgen.sizes().align(ty)); + self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty); + } + }; + + if self.async_ { + let mut params = Vec::new(); + for (_, ty) in func.params.iter() { + self.resolve.push_flat(ty, &mut params); + } + + let name = &format!("[async-start]{}", func.name); + + if params.len() > MAX_FLAT_RESULTS { + let (size, align) = self + .bindgen + .sizes() + .params(func.params.iter().map(|(_, ty)| ty)); + let ptr = self.bindgen.return_pointer(size, align); + self.stack.push(ptr.clone()); + self.emit(&Instruction::AsyncCallStart { + name, + params: &[WasmType::Pointer], + results: &[], + }); + self.stack.push(ptr); + read_from_memory(self); + } else { + self.emit(&Instruction::AsyncCallStart { + name, + params: &[], + results: ¶ms, + }); + for (_, ty) in func.params.iter() { + self.lift(ty); + } + } + } else if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that // make-up the type. @@ -900,23 +1061,33 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise argument is read in succession from memory // where the pointer to the arguments is the first argument // to the function. - let mut offset = 0usize; self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); - } + read_from_memory(self); } // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); + self.emit(&Instruction::CallInterface { + func, + async_: self.async_, + }); + + let (lower_to_memory, async_results) = if self.async_ { + self.emit(&Instruction::AsyncPostCallInterface { func }); - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. + let mut results = Vec::new(); + for ty in func.results.iter_types() { + self.resolve.push_flat(ty, &mut results); + } + (results.len() > MAX_FLAT_PARAMS, Some(results)) + } else { + (sig.retptr, None) + }; + + // This was dynamically allocated by the caller (or async start + // function) so after it's been read by the guest we need to + // deallocate it. if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { + if sig.indirect_params && !self.async_ { let (size, align) = self .bindgen .sizes() @@ -926,7 +1097,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if !sig.retptr { + if !lower_to_memory { // With no return pointer in use we simply lower the // result(s) and return that directly from the function. let results = self @@ -965,13 +1136,31 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } } } - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); + if let Some(results) = async_results { + let name = &format!("[async-return]{}", func.name); + + self.emit(&Instruction::AsyncCallReturn { + name, + params: &if results.len() > MAX_FLAT_PARAMS { + vec![WasmType::Pointer] + } else { + results + }, + }); + self.emit(&Instruction::Return { func, amt: 1 }); + } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + } } } @@ -1168,8 +1357,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { results: &results, }); } - TypeDefKind::Future(_) => todo!("lower future"), - TypeDefKind::Stream(_) => todo!("lower stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLower { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1353,8 +1555,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&ResultLift { result: r, ty: id }); } - TypeDefKind::Future(_) => todo!("lift future"), - TypeDefKind::Stream(_) => todo!("lift stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLift { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1422,7 +1637,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset), TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), // Decompose the record into its components and then write all // the components into memory one-by-one. @@ -1512,8 +1730,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.store_intrepr(offset, e.tag()); } - TypeDefKind::Future(_) => todo!("write future to memory"), - TypeDefKind::Stream(_) => todo!("write stream to memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1611,7 +1827,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), TypeDefKind::Resource => { todo!(); @@ -1695,8 +1914,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - TypeDefKind::Future(_) => todo!("read future from memory"), - TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1868,6 +2085,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) => todo!("read future from memory"), TypeDefKind::Stream(_) => todo!("read stream from memory"), + TypeDefKind::Error => todo!("read error from memory"), TypeDefKind::Unknown => unreachable!(), }, } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index fae3f3b90..07f8dcf75 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -20,6 +20,223 @@ pub enum Direction { Export, } +// #[derive(Default)] +// pub struct Types { +// type_info: HashMap, +// } + +// #[derive(Default, Clone, Copy, Debug)] +// pub struct TypeInfo { +// /// Whether or not this type is ever used (transitively) within the +// /// parameter of an imported function. +// /// +// /// This means that it's used in a context where ownership isn't +// /// relinquished. +// pub borrowed: bool, + +// /// Whether or not this type is ever used (transitively) within the +// /// parameter or result of an export, or the result of an import. +// /// +// /// This means that it's used in a context where ownership is required and +// /// memory management is necessary. +// pub owned: bool, + +// /// Whether or not this type is ever used (transitively) within the +// /// error case in the result of a function. +// pub error: bool, + +// /// Whether or not this type (transitively) has a list (or string). +// pub has_list: bool, + +// /// Whether or not this type (transitively) has a resource (or handle). +// pub has_resource: bool, + +// /// Whether or not this type (transitively) has a borrow handle. +// pub has_borrow_handle: bool, + +// /// Whether or not this type (transitively) has an own handle. +// pub has_own_handle: bool, +// } + +// impl std::ops::BitOrAssign for TypeInfo { +// fn bitor_assign(&mut self, rhs: Self) { +// self.borrowed |= rhs.borrowed; +// self.owned |= rhs.owned; +// self.error |= rhs.error; +// self.has_list |= rhs.has_list; +// self.has_resource |= rhs.has_resource; +// self.has_borrow_handle |= rhs.has_borrow_handle; +// self.has_own_handle |= rhs.has_own_handle; +// } +// } + +// impl TypeInfo { +// pub fn is_clone(&self) -> bool { +// !self.has_resource +// } +// pub fn is_copy(&self) -> bool { +// !self.has_list && !self.has_resource +// } +// } + +// impl Types { +// pub fn analyze(&mut self, resolve: &Resolve) { +// for (t, _) in resolve.types.iter() { +// self.type_id_info(resolve, t); +// } +// for (_, world) in resolve.worlds.iter() { +// for (import, (_, item)) in world +// .imports +// .iter() +// .map(|i| (true, i)) +// .chain(world.exports.iter().map(|i| (false, i))) +// { +// match item { +// WorldItem::Function(f) => { +// self.type_info_func(resolve, f, import); +// } +// WorldItem::Interface(id) => { +// for (_, f) in resolve.interfaces[*id].functions.iter() { +// self.type_info_func(resolve, f, import); +// } +// } +// WorldItem::Type(_) => {} +// } +// } +// } +// } + +// fn type_info_func(&mut self, resolve: &Resolve, func: &Function, import: bool) { +// let mut live = LiveTypes::default(); +// for (_, ty) in func.params.iter() { +// self.type_info(resolve, ty); +// live.add_type(resolve, ty); +// } +// for id in live.iter() { +// if resolve.types[id].name.is_some() { +// let info = self.type_info.get_mut(&id).unwrap(); +// if import { +// info.borrowed = true; +// } else { +// info.owned = true; +// } +// } +// } +// let mut live = LiveTypes::default(); +// for ty in func.results.iter_types() { +// self.type_info(resolve, ty); +// live.add_type(resolve, ty); +// } +// for id in live.iter() { +// if resolve.types[id].name.is_some() { +// self.type_info.get_mut(&id).unwrap().owned = true; +// } +// } + +// for ty in func.results.iter_types() { +// let id = match ty { +// Type::Id(id) => *id, +// _ => continue, +// }; +// let err = match &resolve.types[id].kind { +// TypeDefKind::Result(Result_ { err, .. }) => err, +// _ => continue, +// }; +// if let Some(Type::Id(id)) = err { +// // When an interface `use`s a type from another interface, it creates a new typeid +// // referring to the definition typeid. Chase any chain of references down to the +// // typeid of the definition. +// fn resolve_type_definition_id(resolve: &Resolve, mut id: TypeId) -> TypeId { +// loop { +// match resolve.types[id].kind { +// TypeDefKind::Type(Type::Id(def_id)) => id = def_id, +// _ => return id, +// } +// } +// } +// let id = resolve_type_definition_id(resolve, *id); +// self.type_info.get_mut(&id).unwrap().error = true; +// } +// } +// } + +// pub fn get(&self, id: TypeId) -> TypeInfo { +// self.type_info[&id] +// } + +// pub fn type_id_info(&mut self, resolve: &Resolve, ty: TypeId) -> TypeInfo { +// if let Some(info) = self.type_info.get(&ty) { +// return *info; +// } +// let mut info = TypeInfo::default(); +// match &resolve.types[ty].kind { +// TypeDefKind::Record(r) => { +// for field in r.fields.iter() { +// info |= self.type_info(resolve, &field.ty); +// } +// } +// TypeDefKind::Resource => { +// info.has_resource = true; +// } +// TypeDefKind::Handle(handle) => { +// match handle { +// Handle::Borrow(_) => info.has_borrow_handle = true, +// Handle::Own(_) => info.has_own_handle = true, +// } +// info.has_resource = true; +// } +// TypeDefKind::Tuple(t) => { +// for ty in t.types.iter() { +// info |= self.type_info(resolve, ty); +// } +// } +// TypeDefKind::Flags(_) => {} +// TypeDefKind::Enum(_) => {} +// TypeDefKind::Variant(v) => { +// for case in v.cases.iter() { +// info |= self.optional_type_info(resolve, case.ty.as_ref()); +// } +// } +// TypeDefKind::List(ty) => { +// info = self.type_info(resolve, ty); +// info.has_list = true; +// } +// TypeDefKind::Type(ty) => { +// info = self.type_info(resolve, ty); +// } +// TypeDefKind::Option(ty) => { +// info = self.type_info(resolve, ty); +// } +// TypeDefKind::Result(r) => { +// info = self.optional_type_info(resolve, r.ok.as_ref()); +// info |= self.optional_type_info(resolve, r.err.as_ref()); +// } +// TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => {} +// TypeDefKind::Unknown => unreachable!(), +// } +// let prev = self.type_info.insert(ty, info); +// assert!(prev.is_none()); +// info +// } + +// pub fn type_info(&mut self, resolve: &Resolve, ty: &Type) -> TypeInfo { +// let mut info = TypeInfo::default(); +// match ty { +// Type::String => info.has_list = true, +// Type::Id(id) => return self.type_id_info(resolve, *id), +// _ => {} +// } +// info +// } + +// fn optional_type_info(&mut self, resolve: &Resolve, ty: Option<&Type>) -> TypeInfo { +// match ty { +// Some(ty) => self.type_info(resolve, ty), +// None => TypeInfo::default(), +// } +// } +// } + pub trait WorldGenerator { fn generate(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { let world = &resolve.worlds[id]; @@ -177,6 +394,7 @@ pub trait InterfaceGenerator<'a> { TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), + TypeDefKind::Error => todo!("generate for error"), TypeDefKind::Unknown => unreachable!(), } } @@ -191,8 +409,9 @@ pub trait AnonymousTypeGenerator<'a> { fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, docs: &Docs); fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_future(&mut self, id: TypeId, ty: &Option, docs: &Docs); - fn anonymous_type_stream(&mut self, id: TypeId, ty: &Stream, docs: &Docs); + fn anonymous_type_stream(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_typ_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_error(&mut self, id: TypeId, docs: &Docs); fn define_anonymous_type(&mut self, id: TypeId) { let ty = &self.resolve().types[id]; @@ -212,6 +431,7 @@ pub trait AnonymousTypeGenerator<'a> { TypeDefKind::Future(f) => self.anonymous_type_future(id, f, &ty.docs), TypeDefKind::Stream(s) => self.anonymous_type_stream(id, s, &ty.docs), TypeDefKind::Handle(handle) => self.anonymous_type_handle(id, handle, &ty.docs), + TypeDefKind::Error => self.anonymous_type_error(id, &ty.docs), TypeDefKind::Unknown => unreachable!(), } } diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index a0862f5e8..5f78a7b73 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -197,10 +197,10 @@ impl Types { info = self.optional_type_info(resolve, ty.as_ref()); } TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); + info = self.type_info(resolve, stream); } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => {} } let prev = self.type_info.insert(ty, info); assert!(prev.is_none()); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index d454505d0..4ea3348fc 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1090,6 +1090,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -1209,6 +1210,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -2727,7 +2729,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let module = self.gen.name; let func_name = self.func_name.to_upper_camel_case(); let interface_name = CSharp::get_class_name_from_qualified_name(module).1; @@ -3027,6 +3029,19 @@ impl Bindgen for FunctionBindgen<'_, '_> { } results.push(resource); } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), + Instruction::FutureLower { .. } => todo!(), + Instruction::FutureLift { .. } => todo!(), + Instruction::StreamLower { .. } => todo!(), + Instruction::StreamLift { .. } => todo!(), + Instruction::ErrorLower { .. } => todo!(), + Instruction::ErrorLift { .. } => todo!(), + Instruction::AsyncCallWasm { .. } => todo!(), + Instruction::Flush { .. } => todo!(), } } diff --git a/crates/go/src/bindgen.rs b/crates/go/src/bindgen.rs index f045bbd00..6d5bd2d55 100644 --- a/crates/go/src/bindgen.rs +++ b/crates/go/src/bindgen.rs @@ -394,6 +394,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } } a => { @@ -678,6 +679,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } } a => { diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index ba7e150cb..2b8b4efbe 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -322,8 +322,7 @@ impl InterfaceGenerator<'_> { TypeDefKind::Stream(t) => { let mut src = String::new(); src.push_str("Stream"); - src.push_str(&self.optional_ty_name(t.element.as_ref())); - src.push_str(&self.optional_ty_name(t.end.as_ref())); + src.push_str(&self.ty_name(t)); src.push('T'); src } @@ -347,6 +346,7 @@ impl InterfaceGenerator<'_> { src } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } } } @@ -679,6 +679,7 @@ impl InterfaceGenerator<'_> { TypeDefKind::Future(_) => todo!("anonymous_type for future"), TypeDefKind::Stream(_) => todo!("anonymous_type for stream"), TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index d4de77f67..598d864f4 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{braced, token, LitStr, Token}; use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; -use wit_bindgen_rust::{Opts, Ownership, WithOption}; +use wit_bindgen_rust::{AsyncConfig, Opts, Ownership, WithOption}; #[proc_macro] pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +46,7 @@ struct Config { resolve: Resolve, world: WorldId, files: Vec, + debug: bool, } /// The source of the wit package definition @@ -63,6 +64,8 @@ impl Parse for Config { let mut world = None; let mut source = None; let mut features = Vec::new(); + let mut async_configured = false; + let mut debug = false; if input.peek(token::Brace) { let content; @@ -137,6 +140,16 @@ impl Parse for Config { Opt::Features(f) => { features.extend(f.into_iter().map(|f| f.value())); } + Opt::Debug(enable) => { + debug = enable.value(); + } + Opt::Async(val, span) => { + if async_configured { + return Err(Error::new(span, "cannot specify second async config")); + } + async_configured = true; + opts.async_ = val; + } } } } else { @@ -157,6 +170,7 @@ impl Parse for Config { resolve, world, files, + debug, }) } } @@ -198,6 +212,8 @@ fn parse_source( None => parse(&vec![root.join("wit")])?, }; + resolve.add_future_and_stream_results(); + Ok((resolve, pkgs, files)) } @@ -215,7 +231,7 @@ impl Config { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if std::env::var("WIT_BINDGEN_DEBUG").is_ok() { + if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &self.resolve.worlds[self.world].name; @@ -273,6 +289,8 @@ mod kw { syn::custom_keyword!(pub_export_macro); syn::custom_keyword!(generate_unused_types); syn::custom_keyword!(features); + syn::custom_keyword!(imports); + syn::custom_keyword!(debug); } #[derive(Clone)] @@ -302,6 +320,11 @@ impl From for wit_bindgen_rust::ExportKey { } } +enum AsyncConfigSomeKind { + Imports, + Exports, +} + enum Opt { World(syn::LitStr), Path(Span, Vec), @@ -325,6 +348,8 @@ enum Opt { PubExportMacro(syn::LitBool), GenerateUnusedTypes(syn::LitBool), Features(Vec), + Async(AsyncConfig, Span), + Debug(syn::LitBool), } impl Parse for Opt { @@ -468,6 +493,34 @@ impl Parse for Opt { syn::bracketed!(contents in input); let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?; Ok(Opt::Features(list.into_iter().collect())) + } else if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse()?)) + } else if l.peek(Token![async]) { + let span = input.parse::()?.span; + input.parse::()?; + if input.peek(syn::LitBool) { + if input.parse::()?.value { + Ok(Opt::Async(AsyncConfig::All, span)) + } else { + Ok(Opt::Async(AsyncConfig::None, span)) + } + } else { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + let contents; + syn::braced!(contents in input); + for (kind, values) in + contents.parse_terminated(parse_async_some_field, Token![,])? + { + match kind { + AsyncConfigSomeKind::Imports => imports = values, + AsyncConfigSomeKind::Exports => exports = values, + } + } + Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span)) + } } else { Err(l.error()) } @@ -526,3 +579,27 @@ fn fmt(input: &str) -> Result { let syntax_tree = syn::parse_file(&input)?; Ok(prettyplease::unparse(&syntax_tree)) } + +fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec)> { + let lookahead = input.lookahead1(); + let kind = if lookahead.peek(kw::imports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Imports + } else if lookahead.peek(kw::exports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Exports + } else { + return Err(lookahead.error()); + }; + + let list; + syn::bracketed!(list in input); + let fields = list.parse_terminated(Parse::parse, Token![,])?; + + Ok(( + kind, + fields.iter().map(|s: &syn::LitStr| s.value()).collect(), + )) +} diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index ae6380003..b3a7e6849 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -413,28 +413,11 @@ impl InterfaceGenerator<'_> { self.push_str("future"); } }, - TypeDefKind::Stream(s) => match (s.element, s.end) { - (Some(element), Some(end)) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(", "); - self.print_ty(&end); - self.push_str(">"); - } - (None, Some(end)) => { - self.push_str("stream<_, "); - self.print_ty(&end); - self.push_str(">"); - } - (Some(element), None) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(">"); - } - (None, None) => { - self.push_str("stream"); - } - }, + TypeDefKind::Stream(s) => { + self.push_str("stream<"); + self.print_ty(s); + self.push_str(">"); + } TypeDefKind::Handle(Handle::Own(ty)) => { self.push_str("own<"); self.print_ty(&Type::Id(*ty)); @@ -446,6 +429,7 @@ impl InterfaceGenerator<'_> { self.push_str(">"); } TypeDefKind::Unknown => unreachable!(), + TypeDefKind::Error => todo!(), } } } diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs new file mode 100644 index 000000000..df3e41996 --- /dev/null +++ b/crates/rust/src/async_support.rs @@ -0,0 +1,411 @@ +use { + futures::{ + channel::oneshot, + future::FutureExt, + sink::Sink, + stream::{FuturesUnordered, Stream, StreamExt}, + }, + once_cell::sync::Lazy, + std::{ + alloc::{self, Layout}, + collections::HashMap, + fmt::{self, Debug, Display}, + future::{Future, IntoFuture}, + marker::PhantomData, + mem::ManuallyDrop, + pin::{pin, Pin}, + ptr, + sync::Arc, + task::{Context, Poll, Wake, Waker}, + }, +}; + +type BoxFuture = Pin + 'static>>; + +struct FutureState(FuturesUnordered); + +static mut CALLS: Lazy>> = Lazy::new(HashMap::new); + +static mut SPAWNED: Vec = Vec::new(); + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + let poll = pin!((*state).0.next()).poll(&mut Context::from_waker(&dummy_waker())); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => break Poll::Ready(()), + Poll::Pending => break Poll::Pending, + } + } else { + (*state).0.extend(SPAWNED.drain(..)); + } + } +} + +pub fn first_poll( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + let state = Box::into_raw(Box::new(FutureState( + [Box::pin(future.map(fun)) as BoxFuture] + .into_iter() + .collect(), + ))); + match unsafe { poll(state) } { + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => state as _, + } +} + +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, + call: *mut u8, +) { + const STATUS_NOT_STARTED: i32 = 0; + const STATUS_PARAMS_READ: i32 = 1; + const STATUS_RESULTS_WRITTEN: i32 = 2; + const STATUS_DONE: i32 = 3; + + match import(params, results, call) { + STATUS_NOT_STARTED => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(*call.cast::(), tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } + STATUS_PARAMS_READ => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(*call.cast::(), tx); + rx.await.unwrap() + } + STATUS_RESULTS_WRITTEN | STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + status => unreachable!(), + } +} + +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const EVENT_CALL_STARTED: i32 = 0; + const EVENT_CALL_RETURNED: i32 = 1; + const EVENT_CALL_DONE: i32 = 2; + + match event0 { + EVENT_CALL_STARTED => { + // TODO: could dealloc params here if we attached the pointer to the call + 1 + } + EVENT_CALL_RETURNED | EVENT_CALL_DONE => { + if let Some(call) = CALLS.remove(&event1) { + call.send(()); + + match poll(ctx as *mut FutureState) { + Poll::Ready(()) => { + drop(Box::from_raw(ctx as *mut FutureState)); + 1 + } + Poll::Pending => 0, + } + } else { + 1 + } + } + _ => unreachable!(), + } +} + +#[doc(hidden)] +pub trait FuturePayload: Sized + 'static { + fn new() -> (u32, u32); + async fn send(sender: u32, value: Self) -> Result<(), Error>; + async fn receive(receiver: u32) -> Result; + fn drop_sender(sender: u32); + fn drop_receiver(receiver: u32); +} + +pub struct FutureSender { + handle: u32, + _phantom: PhantomData, +} + +impl FutureSender { + pub async fn send(self, v: T) -> Result<(), Error> { + T::send(ManuallyDrop::new(self).handle, v).await + } +} + +impl Drop for FutureSender { + fn drop(&mut self) { + T::drop_sender(self.handle) + } +} + +pub struct FutureReceiver { + handle: u32, + _phantom: PhantomData, +} + +impl FutureReceiver { + #[doc(hidden)] + pub fn into_handle(self) -> u32 { + ManuallyDrop::new(self).handle + } +} + +impl IntoFuture for FutureReceiver { + type Output = Result; + type IntoFuture = Pin + 'static>>; + + fn into_future(self) -> Self::IntoFuture { + Box::pin(T::receive(ManuallyDrop::new(self).handle)) + } +} + +impl Drop for FutureReceiver { + fn drop(&mut self) { + T::drop_receiver(self.handle) + } +} + +#[doc(hidden)] +pub trait StreamPayload: Unpin + Sized + 'static { + fn new() -> (u32, u32); + async fn send(sender: u32, values: Vec) -> Result<(), Error>; + async fn receive(receiver: u32) -> Option, Error>>; + fn drop_sender(sender: u32); + fn drop_receiver(receiver: u32); +} + +pub struct StreamSender { + handle: u32, + future: Option> + 'static>>>, + _phantom: PhantomData, +} + +impl Sink> for StreamSender { + type Error = Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if let Some(future) = &mut me.future { + match future.as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Ready(Ok(())) + } + } + + fn start_send(self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + assert!(self.future.is_none()); + self.get_mut().future = Some(Box::pin(T::send(self.handle, item))); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamSender { + fn drop(&mut self) { + T::drop_sender(self.handle) + } +} + +pub struct StreamReceiver { + handle: u32, + future: Option, Error>>> + 'static>>>, + _phantom: PhantomData, +} + +impl StreamReceiver { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { + handle, + future: None, + _phantom: PhantomData, + } + } + + #[doc(hidden)] + pub fn into_handle(self) -> u32 { + ManuallyDrop::new(self).handle + } +} + +impl Stream for StreamReceiver { + type Item = Result, Error>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if me.future.is_none() { + me.future = Some(Box::pin(T::receive(me.handle))); + } + + match me.future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl Drop for StreamReceiver { + fn drop(&mut self) { + T::drop_receiver(self.handle) + } +} + +pub struct Error { + handle: u32, +} + +impl Error { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Error").finish() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for Error {} + +impl Drop for Error { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-drop]"] + fn drop(_: u32); + } + if self.handle != 0 { + unsafe { drop(self.handle) } + } + } + } +} + +pub fn new_future() -> (FutureSender, FutureReceiver) { + let (tx, rx) = T::new(); + ( + FutureSender { + handle: tx, + _phantom: PhantomData, + }, + FutureReceiver { + handle: rx, + _phantom: PhantomData, + }, + ) +} + +pub fn new_stream() -> (StreamSender, StreamReceiver) { + let (tx, rx) = T::new(); + ( + StreamSender { + handle: tx, + future: None, + _phantom: PhantomData, + }, + StreamReceiver { + handle: rx, + future: None, + _phantom: PhantomData, + }, + ) +} + +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} + +fn wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future + 'static) -> T { + let (mut tx, mut rx) = oneshot::channel(); + let state = &mut FutureState( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ); + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => wait(state), + } + } +} diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 0453a07ea..23e2b56cf 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -8,6 +8,8 @@ use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source}; pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -23,10 +25,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, + async_, + wasm_import_module, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -58,14 +64,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import( - &mut self, - module_name: &str, - name: &str, - params: &[WasmType], - results: &[WasmType], - ) -> String { + fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String { // Define the actual function we're calling inline + let tmp = self.tmp(); let mut sig = "(".to_owned(); for param in params.iter() { sig.push_str("_: "); @@ -78,6 +79,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let module_name = self.wasm_import_module; uwrite!( self.src, " @@ -85,14 +87,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ #[link_name = \"{name}\"] - fn wit_import{sig}; + fn wit_import{tmp}{sig}; }} #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} + extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + format!("wit_import{tmp}") } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -456,6 +458,43 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(result); } + Instruction::FutureLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::FutureLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::FutureReceiver::from_handle({op} as u32)" + )) + } + + Instruction::StreamLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::StreamLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::StreamReceiver::from_handle({op} as u32)" + )) + } + + Instruction::ErrorLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).handle() as i32")) + } + + Instruction::ErrorLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!("{async_support}::Error::from_handle({op} as u32)")) + } + Instruction::RecordLower { ty, record, .. } => { self.record_lower(*ty, record, &operands[0], results); } @@ -779,12 +818,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), - name, - &sig.params, - &sig.results, - ); + let func = self.declare_import(name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -797,8 +831,32 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(");\n"); } + Instruction::AsyncCallWasm { name, size, align } => { + let func = self.declare_import(name, &[WasmType::Pointer; 3], &[WasmType::I32]); + + let async_support = self.gen.path_to_async_support(); + let tmp = self.tmp(); + let layout = format!("layout{tmp}"); + let alloc = self.gen.path_to_std_alloc_module(); + self.push_str(&format!( + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + )); + let operands = operands.join(", "); + uwriteln!( + self.src, + "{async_support}::await_result({func}, {layout}, {operands}).await;" + ); + } + Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); + if self.async_ { + let tmp = self.tmp(); + let result = format!("result{tmp}"); + self.push_str(&format!("let {result} = ")); + results.push(result); + } else { + self.let_results(func.results.len(), results); + }; match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); @@ -839,6 +897,76 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(";\n"); } + Instruction::AsyncMalloc { size, align } => { + let alloc = self.gen.path_to_std_alloc_module(); + let tmp = self.tmp(); + let ptr = format!("ptr{tmp}"); + let layout = format!("layout{tmp}"); + uwriteln!( + self.src, + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); + let {ptr} = {alloc}::alloc({layout});" + ); + results.push(ptr); + } + + Instruction::AsyncCallStart { + name, + params, + results: call_results, + } => { + let func = self.declare_import(name, params, call_results); + + if !call_results.is_empty() { + self.push_str("let ret = "); + results.push("ret".to_string()); + } + uwriteln!(self.src, "{func}({});", operands.join(", ")); + } + + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + results.push("result".into()); + let params = (0..func.results.len()) + .map(|_| { + let tmp = self.tmp(); + let param = format!("result{}", tmp); + results.push(param.clone()); + param + }) + .collect::>() + .join(", "); + let async_support = self.gen.path_to_async_support(); + uwriteln!( + self.src, + "\ + let result = {async_support}::first_poll({result}, |{params}| {{ + " + ); + } + + Instruction::AsyncCallReturn { name, params } => { + let func = self.declare_import(name, params, &[]); + + uwriteln!( + self.src, + "\ + {func}({}); + }}); + ", + operands.join(", ") + ); + } + + Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "let {result} = {};", operands[i]); + results.push(result); + } + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { @@ -868,7 +996,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = i32::from(*{}.add({offset}).cast::());", + "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0] ); results.push(format!("l{tmp}")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 2eb5dc870..cd044e2e6 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,7 +1,7 @@ use crate::bindgen::FunctionBindgen; use crate::{ - int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier, InterfaceName, - Ownership, RuntimeItem, RustFlagsRepr, RustWasm, + int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, + InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, }; use anyhow::Result; use heck::*; @@ -19,7 +19,7 @@ pub struct InterfaceGenerator<'a> { pub in_import: bool, pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, - pub wasm_import_module: Option<&'a str>, + pub wasm_import_module: &'a str, pub resolve: &'a Resolve, pub return_pointer_area_size: usize, pub return_pointer_area_align: usize, @@ -156,6 +156,17 @@ impl InterfaceGenerator<'_> { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let resource = match func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(id) @@ -163,12 +174,13 @@ impl InterfaceGenerator<'_> { | FunctionKind::Static(id) => Some(id), }; - funcs_to_export.push((func, resource)); + funcs_to_export.push((func, resource, async_)); let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name); + self.generate_guest_export(func, interface.map(|(_, k)| k), &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -177,7 +189,7 @@ impl InterfaceGenerator<'_> { sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, false); self.src.push_str(";\n"); let trait_method = mem::replace(&mut self.src, prev); methods.push(trait_method); @@ -188,9 +200,9 @@ impl InterfaceGenerator<'_> { self.generate_interface_trait( &name, &methods, - traits.iter().map(|(resource, (trait_name, _methods))| { - (resource.unwrap(), trait_name.as_str()) - }), + traits + .iter() + .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), ) } @@ -259,7 +271,7 @@ fn _resource_rep(handle: u32) -> *mut u8 None => { let world = match self.identifier { Identifier::World(w) => w, - Identifier::Interface(..) => unreachable!(), + Identifier::None | Identifier::Interface(..) => unreachable!(), }; let world = self.resolve.worlds[world].name.to_snake_case(); format!("__export_world_{world}_cabi") @@ -292,7 +304,7 @@ macro_rules! {macro_name} {{ " ); - for (func, resource) in funcs_to_export { + for (func, resource, async_) in funcs_to_export { let ty = match resource { None => "$ty".to_string(), Some(id) => { @@ -304,13 +316,13 @@ macro_rules! {macro_name} {{ format!("<$ty as $($path_to_types)*::Guest>::{name}") } }; - self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*"); + self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), - Identifier::World(_) => unreachable!(), + Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); uwriteln!( @@ -357,9 +369,13 @@ macro_rules! {macro_name} {{ uwriteln!(self.src, "}}"); } - pub fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + pub fn generate_imports<'a>( + &mut self, + funcs: impl Iterator, + interface: Option<&WorldKey>, + ) { for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, interface); } } @@ -445,12 +461,320 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_payloads(&mut self, prefix: &str, func: &Function, interface: Option<&WorldKey>) { + for (index, ty) in func + .find_futures_and_streams(self.resolve) + .into_iter() + .enumerate() + { + let payload_result = self.gen.payload_results[&ty]; + let module = format!( + "{prefix}{}", + interface + .map(|name| self.resolve.name_world_key(name)) + .unwrap_or_else(|| "$root".into()) + ); + let func_name = &func.name; + let type_mode = TypeMode { + lifetime: None, + lists_borrowed: false, + style: TypeOwnershipStyle::Owned, + }; + let async_support = self.path_to_async_support(); + + match &self.resolve.types[ty].kind { + TypeDefKind::Future(payload_type) => { + let (name, full_name) = if let Some(payload_type) = payload_type { + ( + { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }, + { + let old = mem::take(&mut self.src); + let old_identifier = + mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }, + ) + } else { + ("()".into(), "()".into()) + }; + + if self.gen.future_payloads_emitted.insert(full_name) { + let send = Function { + name: format!("[future-send-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: if let Some(payload_type) = payload_type { + vec![ + ("sender".into(), Type::U32), + ("value".into(), *payload_type), + ] + } else { + vec![("sender".into(), Type::U32)] + }, + results: Results::Anon(Type::Id(self.gen.unit_result.unwrap())), + docs: Default::default(), + stability: Stability::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &send, + if payload_type.is_some() { + vec!["sender".into(), "value".into()] + } else { + vec!["sender".into()] + }, + true, + ); + let send = String::from(mem::replace(&mut self.src, old)); + + let receive = Function { + name: format!("[future-receive-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("receiver".into(), Type::U32)], + results: Results::Anon(Type::Id(payload_result)), + docs: Default::default(), + stability: Stability::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &receive, + vec!["receiver".into()], + true, + ); + let receive = String::from(mem::replace(&mut self.src, old)); + + uwriteln!( + self.src, + r#" +impl {async_support}::FuturePayload for {name} {{ + fn new() -> (u32, u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-new-{index}]{func_name}"] + fn new(_: *mut u32); + }} + let mut results = [0u32; 2]; + unsafe {{ new(results.as_mut_ptr()) }}; + (results[0], results[1]) + }} + }} + + async fn send(sender: u32, value: Self) -> Result<(), {async_support}::Error> {{ + unsafe {{ {send} }} + }} + + async fn receive(receiver: u32) -> Result {{ + unsafe {{ {receive} }} + }} + + fn drop_sender(sender: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-sender-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(sender) }} + }} + }} + + fn drop_receiver(receiver: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-receiver-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(receiver) }} + }} + }} +}} + "#, + ); + } + } + TypeDefKind::Stream(payload_type) => { + let name = { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }; + + let full_name = { + let old = mem::take(&mut self.src); + let old_identifier = mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }; + + let TypeDefKind::Option(Type::Id(result_ty)) = + &self.resolve.types[payload_result].kind + else { + unreachable!() + }; + let TypeDefKind::Result(Result_ { + ok: Some(list_ty), .. + }) = &self.resolve.types[*result_ty].kind + else { + unreachable!() + }; + + if self.gen.stream_payloads_emitted.insert(full_name) { + let send = Function { + name: format!("[stream-send-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("sender".into(), Type::U32), ("values".into(), *list_ty)], + results: Results::Anon(Type::Id(self.gen.unit_result.unwrap())), + docs: Default::default(), + stability: Stability::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &send, + vec!["sender".into(), "values".into()], + true, + ); + let send = String::from(mem::replace(&mut self.src, old)); + + let receive = Function { + name: format!("[stream-receive-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("receiver".into(), Type::U32)], + results: Results::Anon(Type::Id(payload_result)), + docs: Default::default(), + stability: Stability::default(), + }; + let old_src = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &receive, + vec!["receiver".into()], + true, + ); + let receive = String::from(mem::replace(&mut self.src, old_src)); + + uwriteln!( + self.src, + r#" +impl {async_support}::StreamPayload for {name} {{ + fn new() -> (u32, u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-new-{index}]{func_name}"] + fn new(_: *mut u32); + }} + let mut results = [0u32; 2]; + unsafe {{ new(results.as_mut_ptr()) }}; + (results[0], results[1]) + }} + }} + + async fn send(sender: u32, values: Vec) -> Result<(), {async_support}::Error> {{ + unsafe {{ {send} }} + }} + + async fn receive(receiver: u32) -> Option, {async_support}::Error>> {{ + unsafe {{ {receive} }} + }} + + fn drop_sender(sender: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-sender-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(sender) }} + }} + }} + + fn drop_receiver(receiver: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-receiver-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(receiver) }} + }} + }} +}} + "# + ); + } + } + _ => unreachable!(), + } + } + } + + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } - let mut sig = FnSig::default(); + self.generate_payloads("[import-payload]", func, interface); + + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }), + }; + let mut sig = FnSig { + async_, + ..Default::default() + }; match func.kind { FunctionKind::Freestanding => {} FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => { @@ -465,17 +789,38 @@ macro_rules! {macro_name} {{ } } self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); - let params = self.print_signature(func, false, &sig); + let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params); + self.generate_guest_import_body(&self.wasm_import_module, func, params, async_); + + self.src.push_str("}\n"); + self.src.push_str("}\n"); + + match func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + self.src.push_str("}\n"); + } + } + } + + fn generate_guest_import_body( + &mut self, + module: &str, + func: &Function, + params: Vec, + async_: bool, + ) { + let mut f = FunctionBindgen::new(self, params, async_, module); abi::call( f.gen.resolve, AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -502,29 +847,33 @@ macro_rules! {macro_name} {{ ); } self.src.push_str(&String::from(src)); - - self.src.push_str("}\n"); - self.src.push_str("}\n"); - - match func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - self.src.push_str("}\n"); - } - } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str) { + fn generate_guest_export( + &mut self, + func: &Function, + interface: Option<&WorldKey>, + trait_name: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); + + self.generate_payloads("[export-payload]", func, interface); + uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_{name_snake}_cabi\ -", + ", ); - let params = self.print_export_sig(func); + let params = if async_ { + self.push_str("() -> *mut u8"); + Vec::new() + } else { + self.print_export_sig(func) + }; self.push_str(" {"); if !self.gen.opts.disable_run_ctors_once_workaround { @@ -543,13 +892,14 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::call( f.gen.resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -565,20 +915,32 @@ macro_rules! {macro_name} {{ self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { + if async_ { + let async_support = self.path_to_async_support(); + uwrite!( + self.src, + "\ + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {async_support}::callback(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_{name_snake}\ -" + " ); let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params); - abi::post_return(f.gen.resolve, func, &mut f); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); + abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, src, @@ -592,14 +954,26 @@ macro_rules! {macro_name} {{ } } - fn generate_raw_cabi_export(&mut self, func: &Function, ty: &str, path_to_self: &str) { + fn generate_raw_cabi_export( + &mut self, + func: &Function, + ty: &str, + path_to_self: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), Identifier::World(_) => None, + Identifier::None => unreachable!(), }; let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); let export_name = func.core_export_name(wasm_module_export_name.as_deref()); + let export_name = if async_ { + format!("[async]{export_name}") + } else { + export_name.to_string() + }; uwrite!( self.src, "\ @@ -608,7 +982,12 @@ macro_rules! {macro_name} {{ ", ); - let params = self.print_export_sig(func); + let params = if async_ { + self.push_str("() -> *mut u8"); + Vec::new() + } else { + self.print_export_sig(func) + }; self.push_str(" {\n"); uwriteln!( self.src, @@ -617,8 +996,18 @@ macro_rules! {macro_name} {{ ); self.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { - let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + if async_ { + uwrite!( + self.src, + "\ + #[export_name = \"{export_prefix}[callback]{export_name}\"] + unsafe extern \"C\" fn _callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {path_to_self}::__callback_{name_snake}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -731,7 +1120,7 @@ macro_rules! {macro_name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, true); self.src.push_str("{ unreachable!() }\n"); } @@ -789,12 +1178,18 @@ macro_rules! {macro_name} {{ // } } - fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec { - let params = self.print_docs_and_params(func, params_owned, sig); + fn print_signature( + &mut self, + func: &Function, + params_owned: bool, + sig: &FnSig, + use_async_sugar: bool, + ) -> Vec { + let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { self.push_str(" -> Self") } else { - self.print_results(&func.results); + self.print_results(&func.results, sig.async_ && !use_async_sugar); } params } @@ -804,6 +1199,7 @@ macro_rules! {macro_name} {{ func: &Function, params_owned: bool, sig: &FnSig, + use_async_sugar: bool, ) -> Vec { self.rustdoc(&func.docs); self.rustdoc_params(&func.params, "Parameters"); @@ -816,7 +1212,7 @@ macro_rules! {macro_name} {{ if sig.unsafe_ { self.push_str("unsafe "); } - if sig.async_ { + if sig.async_ && use_async_sugar { self.push_str("async "); } self.push_str("fn "); @@ -906,18 +1302,24 @@ macro_rules! {macro_name} {{ params } - fn print_results(&mut self, results: &Results) { + fn print_results(&mut self, results: &Results, async_: bool) { + self.push_str(" -> "); + if async_ { + self.push_str("impl ::core::future::Future {} + 0 => { + self.push_str("()"); + } 1 => { - self.push_str(" -> "); let ty = results.iter_types().next().unwrap(); let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); self.print_ty(ty, mode); } _ => { - self.push_str(" -> ("); + self.push_str("("); for ty in results.iter_types() { let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); @@ -927,6 +1329,10 @@ macro_rules! {macro_name} {{ self.push_str(")") } } + + if async_ { + self.push_str("> + 'static"); + } } /// Calculates the `TypeMode` to be used for the `ty` specified. @@ -1854,6 +2260,10 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } + pub fn path_to_async_support(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "async_support") + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, @@ -1911,11 +2321,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { }} "# ); - self.wasm_import_module.unwrap().to_string() + self.wasm_import_module.to_string() } else { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), Identifier::World(_) => unimplemented!("resource exports from worlds"), + Identifier::None => unreachable!(), }; let box_path = self.path_to_box(); uwriteln!( @@ -2256,18 +2667,23 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< } fn anonymous_type_future(&mut self, _id: TypeId, ty: &Option, _docs: &Docs) { - self.interface.push_str("Future<"); + let async_support = self.interface.path_to_async_support(); + self.interface + .push_str(&format!("{async_support}::FutureReceiver<")); self.interface.print_optional_ty(ty.as_ref(), self.mode); self.interface.push_str(">"); } - fn anonymous_type_stream(&mut self, _id: TypeId, stream: &Stream, _docs: &Docs) { - self.interface.push_str("Stream<"); + fn anonymous_type_stream(&mut self, _id: TypeId, stream: &Type, _docs: &Docs) { + let async_support = self.interface.path_to_async_support(); self.interface - .print_optional_ty(stream.element.as_ref(), self.mode); - self.interface.push_str(","); - self.interface - .print_optional_ty(stream.end.as_ref(), self.mode); + .push_str(&format!("{async_support}::StreamReceiver<")); + self.interface.print_ty(stream, self.mode); self.interface.push_str(">"); } + + fn anonymous_type_error(&mut self, _id: TypeId, _docs: &Docs) { + let async_support = self.interface.path_to_async_support(); + self.interface.push_str(&format!("{async_support}::Error")); + } } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index de76c75e8..8d32759e5 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -46,6 +46,11 @@ struct RustWasm { export_macros: Vec<(String, String)>, /// Interface names to how they should be generated with: GenerationConfiguration, + + unit_result: Option, + payload_results: HashMap, + future_payloads_emitted: HashSet, + stream_payloads_emitted: HashSet, } #[derive(Default)] @@ -97,6 +102,7 @@ enum RuntimeItem { AsF64, ResourceType, BoxType, + AsyncSupport, } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -117,6 +123,23 @@ fn parse_with(s: &str) -> Result<(String, WithOption), String> { Ok((k.to_string(), v)) } +#[derive(Default, Debug, Clone)] +pub enum AsyncConfig { + #[default] + None, + Some { + imports: Vec, + exports: Vec, + }, + All, +} + +#[cfg(feature = "clap")] +fn parse_async(s: &str) -> Result { + _ = s; + Err("todo: parse `AsyncConfig`".into()) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -226,6 +249,10 @@ pub struct Opts { /// Whether to generate unused structures, not generated by default (false) #[cfg_attr(feature = "clap", arg(long))] pub generate_unused_types: bool, + + /// Determines which functions to lift or lower `async`, if any. + #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async))] + pub async_: AsyncConfig, } impl Opts { @@ -245,7 +272,7 @@ impl RustWasm { fn interface<'a>( &'a mut self, identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, + wasm_import_module: &'a str, resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { @@ -367,6 +394,9 @@ impl RustWasm { } } self.src.push_str("}\n"); + if emitted.contains(&RuntimeItem::AsyncSupport) { + self.src.push_str("pub use _rt::async_support;\n"); + } } fn emit_runtime_item(&mut self, item: RuntimeItem) { @@ -599,6 +629,12 @@ impl Drop for Resource { "#, ); } + + RuntimeItem::AsyncSupport => { + self.src.push_str("pub mod async_support {"); + self.src.push_str(include_str!("async_support.rs")); + self.src.push_str("}"); + } } } @@ -887,6 +923,10 @@ impl WorldGenerator for RustWasm { self.with.insert(k.clone(), v.clone().into()); } self.with.generate_by_default = self.opts.generate_all; + + let (unit_result, payload_results) = resolve.find_future_and_stream_results(); + self.unit_result = unit_result; + self.payload_results = payload_results; } fn import_interface( @@ -900,7 +940,7 @@ impl WorldGenerator for RustWasm { let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( Identifier::Interface(id, name), - Some(&wasm_import_module), + &wasm_import_module, resolve, true, ); @@ -910,7 +950,7 @@ impl WorldGenerator for RustWasm { } gen.types(id); - gen.generate_imports(resolve.interfaces[id].functions.values()); + gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); gen.finish_append_submodule(&snake, module_path); @@ -926,9 +966,9 @@ impl WorldGenerator for RustWasm { ) { self.import_funcs_called = true; - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + gen.generate_imports(funcs.iter().map(|(_, func)| *func), None); let src = gen.finish(); self.src.push_str(&src); @@ -942,7 +982,13 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) -> Result<()> { self.interface_last_seen_as_import.insert(id, false); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); + let mut gen = self.interface( + Identifier::Interface(id, name), + &wasm_import_module, + resolve, + false, + ); let (snake, module_path) = gen.start_append_submodule(name); if gen.gen.name_interface(resolve, id, name, true)? { return Ok(()); @@ -956,7 +1002,12 @@ impl WorldGenerator for RustWasm { if self.opts.stubs { let world_id = self.world.unwrap(); - let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + let mut gen = self.interface( + Identifier::World(world_id), + &wasm_import_module, + resolve, + false, + ); gen.generate_stub(Some((id, name)), resolve.interfaces[id].functions.values()); let stub = gen.finish(); self.src.push_str(&stub); @@ -971,14 +1022,14 @@ impl WorldGenerator for RustWasm { funcs: &[(&str, &Function)], _files: &mut Files, ) -> Result<()> { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); let macro_name = gen.generate_exports(None, funcs.iter().map(|f| f.1))?; let src = gen.finish(); self.src.push_str(&src); self.export_macros.push((macro_name, String::new())); if self.opts.stubs { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); gen.generate_stub(None, funcs.iter().map(|f| f.1)); let stub = gen.finish(); self.src.push_str(&stub); @@ -993,7 +1044,7 @@ impl WorldGenerator for RustWasm { types: &[(&str, TypeId)], _files: &mut Files, ) { - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); for (name, ty) in types { gen.define_type(name, *ty); } @@ -1134,6 +1185,7 @@ fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> V } enum Identifier<'a> { + None, World(WorldId), Interface(InterfaceId, &'a WorldKey), } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index e42381bc9..917516118 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -501,6 +501,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -570,6 +571,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -623,7 +625,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2028,6 +2030,19 @@ impl Bindgen for FunctionBindgen<'_, '_> { "Memory.free(org.teavm.interop.Address.fromInt({address}), ({length}) * {size}, {align});" ); } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), + Instruction::FutureLower { .. } => todo!(), + Instruction::FutureLift { .. } => todo!(), + Instruction::StreamLower { .. } => todo!(), + Instruction::StreamLift { .. } => todo!(), + Instruction::ErrorLower { .. } => todo!(), + Instruction::ErrorLift { .. } => todo!(), + Instruction::AsyncCallWasm { .. } => todo!(), + Instruction::Flush { .. } => todo!(), } } diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 7e3c74425..2757b4530 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -193,6 +193,7 @@ fn gen_world( } } let (pkgs, _files) = resolve.push_path(&opts.wit)?; + resolve.add_future_and_stream_results(); let world = resolve.select_world(&pkgs, opts.world.as_deref())?; generator.generate(&resolve, world, files)?;