diff --git a/Cargo.lock b/Cargo.lock index 0cf5a8f04..b94faa2f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2181,7 +2181,6 @@ dependencies = [ name = "wit-bindgen-guest-rust" version = "0.2.0" dependencies = [ - "async-trait", "bitflags", "wit-bindgen-guest-rust-macro", ] diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index eab2b603c..c9875af0f 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -977,7 +977,6 @@ impl Generator for C { } fn import(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); let sig = iface.wasm_signature(AbiVariant::GuestImport, func); @@ -1053,7 +1052,6 @@ impl Generator for C { } fn export(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); let sig = iface.wasm_signature(AbiVariant::GuestExport, func); diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 2aa24d03e..a380c0fd7 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -478,7 +478,6 @@ impl Generator for RustWasm { fn import(&mut self, iface: &Interface, func: &Function) { let mut sig = FnSig::default(); let param_mode = TypeMode::AllBorrowed("'_"); - sig.async_ = func.is_async; match &func.kind { FunctionKind::Freestanding => {} FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => { @@ -579,10 +578,6 @@ impl Generator for RustWasm { self.src.push_str("::*;\n"); } - if func.is_async { - self.src.push_str("let future = async move {\n"); - } - let mut f = FunctionBindgen::new(self, params); iface.call( AbiVariant::GuestExport, @@ -597,18 +592,12 @@ impl Generator for RustWasm { } = f; assert!(!needs_cleanup_list); self.src.push_str(&String::from(src)); - if func.is_async { - self.src.push_str("};\n"); - self.src - .push_str("wit_bindgen_guest_rust::rt::execute(Box::pin(future));\n"); - } self.src.push_str("}\n"); let prev = mem::take(&mut self.src); self.in_trait = true; let mut sig = FnSig::default(); sig.private = true; - sig.async_ = func.is_async; match &func.kind { FunctionKind::Freestanding => {} FunctionKind::Static { .. } => sig.use_item_name = true, @@ -659,11 +648,7 @@ impl Generator for RustWasm { fn finish_one(&mut self, iface: &Interface, files: &mut Files) { let mut src = mem::take(&mut self.src); - let any_async = iface.functions.iter().any(|f| f.is_async); for (name, trait_) in self.traits.iter() { - if any_async { - src.push_str("#[wit_bindgen_guest_rust::async_trait(?Send)]\n"); - } src.push_str("pub trait "); src.push_str(&name); src.push_str(" {\n"); @@ -674,9 +659,6 @@ impl Generator for RustWasm { src.push_str("}\n"); for (id, methods) in trait_.resource_methods.iter() { - if any_async { - src.push_str("#[wit_bindgen_guest_rust::async_trait(?Send)]\n"); - } src.push_str(&format!( "pub trait {} {{\n", iface.resources[*id].name.to_camel_case() @@ -1465,79 +1447,6 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str(");\n"); } - Instruction::CallWasmAsyncImport { - iface, - name, - params: wasm_params, - results: wasm_results, - } => { - // The first thing we do here is define the completion callback - // which the host will invoke when the asynchronous call - // actually finishes. This receives our own custom state - // parameter as the first parameter which is the `Sender` - // converted to a `usize`. Afterwards it receives all the - // results which we'll transfer ove the `sender`, the canonical - // ABI of the results. - self.push_str("unsafe extern \"C\" fn completion_callback(sender: usize"); - for (i, result) in wasm_results.iter().enumerate() { - self.push_str(", "); - self.push_str(&format!("ret{}: ", i)); - self.push_str(wasm_type(*result)); - } - self.push_str(") {\n"); - self.push_str("wit_bindgen_guest_rust::rt::Sender::from_usize(sender).send(("); - for i in 0..wasm_results.len() { - self.push_str(&format!("ret{},", i)); - } - self.push_str("));\n"); - self.push_str("}\n"); - - // Next we create the future channel which will be used to track - // the state of this import. The "oneshot" here means that the - // sender (`tx`) will send something once over `rx`. The type of - // the `Oneshot` is the type of the `wasm_results` which is the - // canonical ABI of the results that this function produces. - self.push_str("let (rx, tx) = wit_bindgen_guest_rust::rt::Oneshot::<("); - for ty in *wasm_results { - self.push_str(wasm_type(*ty)); - self.push_str(", "); - } - self.push_str(")>::new();\n"); - - // Then we can actually call the function now that we have - // all the parameters. The first parameters to the import are - // the canonical ABI `operands` we were provided, and the last - // two arguments are our completion callback and the context for - // the callback, our `tx` sender. - let func = self.declare_import(iface, name, wasm_params, &[]); - self.push_str(&func); - self.push_str("("); - for op in operands { - self.push_str(op); - self.push_str(", "); - } - self.push_str("completion_callback as i32, "); - self.push_str("tx.into_usize() as i32"); - self.push_str(");\n"); - - // And finally we want to "appear synchronous" with an async - // function, so we immediately `.await` the results of the - // oneshot. This binds all the canonical ABI results to then get - // translated in further instructions to the result of this - // function call. - let tmp = self.tmp(); - self.push_str("let ("); - for i in 0..wasm_results.len() { - let name = format!("ret{}_{}", tmp, i); - self.push_str(&name); - self.push_str(","); - results.push(name); - } - self.push_str(") = rx.await;\n"); - } - - Instruction::CallWasmAsyncExport { .. } => unreachable!(), - Instruction::CallInterface { module, func } => { self.push_str("let result = "); results.push("result".to_string()); @@ -1573,9 +1482,6 @@ impl Bindgen for FunctionBindgen<'_> { } self.push_str(&operands.join(", ")); self.push_str(")"); - if func.is_async { - self.push_str(".await"); - } self.push_str(";\n"); } @@ -1595,15 +1501,6 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::ReturnAsyncExport { .. } => { - self.emit_cleanup(); - self.push_str(&format!( - "wit_bindgen_guest_rust::rt::async_export_done({}, {});\n", - operands[0], operands[1] - )); - } - Instruction::ReturnAsyncImport { .. } => unreachable!(), - Instruction::I32Load { offset } => { results.push(format!("*(({} + {}) as *const i32)", operands[0], offset)); } diff --git a/crates/gen-guest-spidermonkey-js/src/lib.rs b/crates/gen-guest-spidermonkey-js/src/lib.rs index d366899fa..905fad25b 100644 --- a/crates/gen-guest-spidermonkey-js/src/lib.rs +++ b/crates/gen-guest-spidermonkey-js/src/lib.rs @@ -1050,8 +1050,6 @@ impl Generator for SpiderMonkeyWasm<'_> { } fn import(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); - // Add the raw Wasm import. let wasm_sig = iface.wasm_signature(AbiVariant::GuestImport, func); let type_index = self.intern_type(wasm_sig.clone()); @@ -1087,8 +1085,6 @@ impl Generator for SpiderMonkeyWasm<'_> { } fn export(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); - let wasm_sig = iface.wasm_signature(AbiVariant::GuestExport, func); let type_index = self.intern_type(wasm_sig.clone()); let export_fn_index = self.wit_export(self.exports.len()); @@ -2112,9 +2108,6 @@ impl abi::Bindgen for Bindgen<'_, '_> { // [] } - abi::Instruction::CallWasmAsyncExport { .. } => todo!(), - abi::Instruction::CallWasmAsyncImport { .. } => todo!(), - abi::Instruction::Return { func, amt } => { match self.lift_lower { abi::LiftLower::LowerArgsLiftResults => { @@ -2192,9 +2185,6 @@ impl abi::Bindgen for Bindgen<'_, '_> { abi::Instruction::I32FromBool { .. } => todo!(), abi::Instruction::BoolFromI32 { .. } => todo!(), - abi::Instruction::ReturnAsyncExport { .. } => todo!(), - abi::Instruction::ReturnAsyncImport { .. } => todo!(), - abi::Instruction::Malloc { .. } => todo!(), abi::Instruction::Free { .. } => todo!(), } diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 1523eb071..08f6d9702 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -285,13 +285,7 @@ impl Js { self.print_ty(iface, ty); } self.src.ts("): "); - if func.is_async { - self.src.ts("Promise<"); - } self.print_ty(iface, &func.result); - if func.is_async { - self.src.ts(">"); - } self.src.ts(";\n"); } @@ -646,12 +640,6 @@ impl Generator for Js { } self.src.js(&src.js); - if func.is_async { - // Note that `catch_closure` here is defined by the `CallInterface` - // instruction. - self.src.js("}, catch_closure);\n"); // `.then` block - self.src.js("});\n"); // `with_current_promise` block. - } self.src.js("}"); let src = mem::replace(&mut self.src, prev); @@ -699,9 +687,6 @@ impl Generator for Js { "this._obj".to_string() } }; - if func.is_async { - self.src.js("async "); - } self.src.js(&format!( "{}({}) {{\n", func.item_name().to_mixed_case(), @@ -914,8 +899,7 @@ impl Generator for Js { addToImports(imports: any): void; "); self.src.js("addToImports(imports) {\n"); - let any_async = iface.functions.iter().any(|f| f.is_async); - if self.exported_resources.len() > 0 || any_async { + if self.exported_resources.len() > 0 { self.src .js("if (!(\"canonical_abi\" in imports)) imports[\"canonical_abi\"] = {};\n"); } @@ -942,17 +926,6 @@ impl Generator for Js { class = iface.resources[*r].name.to_camel_case(), )); } - if any_async { - let promises = self.intrinsic(Intrinsic::Promises); - self.src.js(&format!( - " - imports.canonical_abi['async_export_done'] = (ctx, ptr) => {{ - {}.remove(ctx)(ptr >>> 0) - }}; - ", - promises - )); - } self.src.js("}\n"); self.src.ts(&format!( @@ -2164,65 +2137,6 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(");\n"); } - Instruction::CallWasmAsyncExport { - module: _, - name, - params: _, - results: wasm_results, - } => { - self.bind_results(wasm_results.len(), results); - let promises = self.gen.intrinsic(Intrinsic::Promises); - self.src.js(&format!( - "\ - await new Promise((resolve, reject) => {{ - const promise_ctx = {promises}.insert(val => {{ - if (typeof val !== 'number') - return reject(val); - resolve(\ - ", - promises = promises - )); - - if wasm_results.len() > 0 { - self.src.js("["); - let operands = &["val".to_string()]; - let mut results = Vec::new(); - for (i, result) in wasm_results.iter().enumerate() { - if i > 0 { - self.src.js(", "); - } - let method = match result { - WasmType::I32 => "getInt32", - WasmType::I64 => "getBigInt64", - WasmType::F32 => "getFloat32", - WasmType::F64 => "getFloat64", - }; - self.load(method, (i * 8) as i32, operands, &mut results); - self.src.js(&results.pop().unwrap()); - } - self.src.js("]"); - } - - // Finish the blocks from above - self.src.js(");\n"); // `resolve(...)` - self.src.js("});\n"); // `promises.insert(...)` - - let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise); - self.src.js(&with); - self.src.js("(promise_ctx, _prev => {\n"); - self.src.js(&self.src_object); - self.src.js("._exports['"); - self.src.js(&name); - self.src.js("']("); - for op in operands { - self.src.js(op); - self.src.js(", "); - } - self.src.js("promise_ctx);\n"); - self.src.js("});\n"); // call to `with` - self.src.js("});\n"); // `await new Promise(...)` - } - Instruction::CallInterface { module: _, func } => { let call = |me: &mut FunctionBindgen<'_>| match &func.kind { FunctionKind::Freestanding | FunctionKind::Static { .. } => { @@ -2251,31 +2165,9 @@ impl Bindgen for FunctionBindgen<'_> { } }; - if func.is_async { - let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise); - let promises = self.gen.intrinsic(Intrinsic::Promises); - self.src.js(&with); - self.src.js("(null, cur_promise => {\n"); - self.src.js(&format!( - "const catch_closure = e => {}.remove(cur_promise)(e);\n", - promises - )); - call(self); - self.src.js(".then(e => {\n"); - match &func.result { - Type::Unit => { - results.push("".to_string()); - } - _ => { - bind_results(self); - self.src.js("e;\n"); - } - } - } else { - bind_results(self); - call(self); - self.src.js(";\n"); - } + bind_results(self); + call(self); + self.src.js(";\n"); } Instruction::Return { amt, func: _ } => match amt { @@ -2287,33 +2179,6 @@ impl Bindgen for FunctionBindgen<'_> { } }, - Instruction::ReturnAsyncImport { .. } => { - // When we reenter webassembly successfully that means that the - // host's promise resolved without exception. Take the current - // promise index saved as part of `CallInterface` and update the - // `CUR_PROMISE` global with what's currently being executed. - // This'll get reset once the wasm returns again. - // - // Note that the name `cur_promise` used here is introduced in - // the `CallInterface` codegen above in the closure for - // `with_current_promise` which we're using here. - // - // TODO: hardcoding `__indirect_function_table` and no help if - // it's not actually defined. - self.gen.needs_get_export = true; - let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise); - self.src.js(&format!( - "\ - {with}(cur_promise, _prev => {{ - get_export(\"__indirect_function_table\").get({})({}); - }}); - ", - operands[0], - operands[1..].join(", "), - with = with, - )); - } - Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results), Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results), Instruction::F32Load { offset } => self.load("getFloat32", *offset, operands, results), diff --git a/crates/gen-host-wasmtime-py/src/lib.rs b/crates/gen-host-wasmtime-py/src/lib.rs index bc793380e..35591b958 100644 --- a/crates/gen-host-wasmtime-py/src/lib.rs +++ b/crates/gen-host-wasmtime-py/src/lib.rs @@ -352,7 +352,6 @@ impl Generator for WasmtimePy { // so a user "export" uses the "guest import" ABI variant on the inside of // this `Generator` implementation. fn export(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let mut pysig = Source::default(); let mut builder = pysig.builder(&mut self.deps, iface); builder.print_sig(func, self.in_import); @@ -469,7 +468,6 @@ impl Generator for WasmtimePy { // so a user "import" uses the "export" ABI variant on the inside of // this `Generator` implementation. fn import(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let mut func_body = Source::default(); let mut builder = func_body.builder(&mut self.deps, iface); diff --git a/crates/gen-host-wasmtime-rust/src/lib.rs b/crates/gen-host-wasmtime-rust/src/lib.rs index 9f32d9ae3..a5e6e2c09 100644 --- a/crates/gen-host-wasmtime-rust/src/lib.rs +++ b/crates/gen-host-wasmtime-rust/src/lib.rs @@ -1,9 +1,8 @@ use heck::*; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::io::{Read, Write}; use std::mem; use std::process::{Command, Stdio}; -use std::str::FromStr; use wit_bindgen_core::wit_parser::abi::{AbiVariant, Bindgen, Instruction, LiftLower, WasmType}; use wit_bindgen_core::{wit_parser::*, Direction, Files, Generator, Source, TypeInfo, Types}; use wit_bindgen_gen_rust_lib::{ @@ -43,10 +42,8 @@ enum NeededFunction { } struct Import { - is_async: bool, name: String, trait_signature: String, - num_wasm_params: usize, closure: String, } @@ -67,63 +64,12 @@ pub struct Opts { #[cfg_attr(feature = "structopt", structopt(long))] pub tracing: bool, - /// Indicates which functions should be `async`: `all`, `none`, or a - /// comma-separated list. - #[cfg_attr( - feature = "structopt", - structopt(long = "async", default_value = "none") - )] - pub async_: Async, - /// A flag to indicate that all trait methods in imports should return a /// custom trait-defined error. Applicable for import bindings. #[cfg_attr(feature = "structopt", structopt(long))] pub custom_error: bool, } -#[derive(Debug, Clone)] -pub enum Async { - None, - All, - Only(HashSet), -} - -impl Async { - fn includes(&self, name: &str) -> bool { - match self { - Async::None => false, - Async::All => true, - Async::Only(list) => list.contains(name), - } - } - - fn is_none(&self) -> bool { - match self { - Async::None => true, - _ => false, - } - } -} - -impl Default for Async { - fn default() -> Async { - Async::None - } -} - -impl FromStr for Async { - type Err = String; - fn from_str(s: &str) -> Result { - Ok(if s == "all" { - Async::All - } else if s == "none" { - Async::None - } else { - Async::Only(s.split(',').map(|s| s.trim().to_string()).collect()) - }) - } -} - impl Opts { pub fn build(self) -> Wasmtime { let mut r = Wasmtime::new(); @@ -522,7 +468,6 @@ impl Generator for Wasmtime { // so a user "export" uses the "guest import" ABI variant on the inside of // this `Generator` implementation. fn export(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); // Generate the closure that's passed to a `Linker`, the final piece of @@ -546,7 +491,6 @@ impl Generator for Wasmtime { needs_buffer_transaction, needs_functions, closures, - async_intrinsic_called, .. } = f; assert!(cleanup.is_none()); @@ -558,7 +502,6 @@ impl Generator for Wasmtime { let mut fnsig = FnSig::default(); fnsig.private = true; - fnsig.async_ = self.opts.async_.includes(&func.name); fnsig.self_arg = Some(self_arg); self.print_docs_and_params(iface, func, TypeMode::LeafBorrowed("'_"), &fnsig); // The Rust return type may differ from the wasm return type based on @@ -595,20 +538,6 @@ impl Generator for Wasmtime { } self.src.push_str("| {\n"); - // If an intrinsic was called asynchronously, which happens if anything - // in the module could be asynchronous, then we must wrap this host - // import with an async block. Otherwise if the function is itself - // explicitly async then we must also wrap it in an async block. - // - // If none of that happens, then this is fine to be sync because - // everything is sync. - let is_async = if async_intrinsic_called || self.opts.async_.includes(&func.name) { - self.src.push_str("Box::new(async move {\n"); - true - } else { - false - }; - if self.opts.tracing { self.src.push_str(&format!( " @@ -659,9 +588,6 @@ impl Generator for Wasmtime { self.src.push_str(&String::from(src)); - if is_async { - self.src.push_str("})\n"); - } self.src.push_str("}"); let closure = mem::replace(&mut self.src, prev).into(); @@ -669,8 +595,6 @@ impl Generator for Wasmtime { .entry(iface.name.to_string()) .or_insert(Vec::new()) .push(Import { - is_async, - num_wasm_params: sig.params.len(), name: func.name.to_string(), closure, trait_signature, @@ -681,15 +605,9 @@ impl Generator for Wasmtime { // so a user "import" uses the "export" ABI variant on the inside of // this `Generator` implementation. fn import(&mut self, iface: &Interface, func: &Function) { - assert!(!func.is_async, "async not supported yet"); let prev = mem::take(&mut self.src); - // If anything is asynchronous on exports then everything must be - // asynchronous, Wasmtime can't intermix async and sync calls because - // it's unknown whether the wasm module will make an async host call. - let is_async = !self.opts.async_.is_none(); let mut sig = FnSig::default(); - sig.async_ = is_async; sig.self_arg = Some("&self, mut caller: impl wasmtime::AsContextMut".to_string()); self.print_docs_and_params(iface, func, TypeMode::AllBorrowed("'_"), &sig); self.push_str("-> Result<"); @@ -794,26 +712,15 @@ impl Generator for Wasmtime { fn finish_one(&mut self, iface: &Interface, files: &mut Files) { for (module, funcs) in sorted_iter(&self.guest_imports) { let module_camel = module.to_camel_case(); - let is_async = !self.opts.async_.is_none(); - if is_async { - self.src - .push_str("#[wit_bindgen_host_wasmtime_rust::async_trait]\n"); - } self.src.push_str("pub trait "); self.src.push_str(&module_camel); self.src.push_str(": Sized "); - if is_async { - self.src.push_str(" + Send"); - } self.src.push_str("{\n"); if self.all_needed_handles.len() > 0 { for handle in self.all_needed_handles.iter() { self.src.push_str("type "); self.src.push_str(&handle.to_camel_case()); self.src.push_str(": std::fmt::Debug"); - if is_async { - self.src.push_str(" + Send + Sync"); - } self.src.push_str(";\n"); } } @@ -878,7 +785,6 @@ impl Generator for Wasmtime { for (module, funcs) in mem::take(&mut self.guest_imports) { let module_camel = module.to_camel_case(); - let is_async = !self.opts.async_.is_none(); self.push_str("\npub fn add_to_linker(linker: &mut wasmtime::Linker"); self.push_str(", get: impl Fn(&mut T) -> "); if self.all_needed_handles.is_empty() { @@ -889,9 +795,6 @@ impl Generator for Wasmtime { self.push_str("+ Send + Sync + Copy + 'static) -> anyhow::Result<()> \n"); self.push_str("where U: "); self.push_str(&module_camel); - if is_async { - self.push_str(", T: Send,"); - } self.push_str("\n{\n"); if self.needs_get_memory { self.push_str("use wit_bindgen_host_wasmtime_rust::rt::get_memory;\n"); @@ -900,11 +803,7 @@ impl Generator for Wasmtime { self.push_str("use wit_bindgen_host_wasmtime_rust::rt::get_func;\n"); } for f in funcs { - let method = if f.is_async { - format!("func_wrap{}_async", f.num_wasm_params) - } else { - String::from("func_wrap") - }; + let method = String::from("func_wrap"); self.push_str(&format!( "linker.{}(\"{}\", \"{}\", {})?;\n", method, module, f.name, f.closure, @@ -979,11 +878,7 @@ impl Generator for Wasmtime { self.push_str(",\n"); } self.push_str("}\n"); - let bound = if self.opts.async_.is_none() { - "" - } else { - ": Send" - }; + let bound = ""; self.push_str(&format!("impl {} {{\n", bound, name)); if self.exported_resources.len() == 0 { @@ -1005,17 +900,7 @@ impl Generator for Wasmtime { name, )); for r in self.exported_resources.iter() { - let (func_wrap, call, wait, prefix, suffix) = if self.opts.async_.is_none() { - ("func_wrap", "call", "", "", "") - } else { - ( - "func_wrap1_async", - "call_async", - ".await", - "Box::new(async move {", - "})", - ) - }; + let (func_wrap, call, wait, prefix, suffix) = ("func_wrap", "call", "", "", ""); self.src.push_str(&format!( " linker.{func_wrap}( @@ -1074,11 +959,7 @@ impl Generator for Wasmtime { self.push_str("Ok(())\n"); self.push_str("}\n"); - let (async_fn, instantiate, wait) = if self.opts.async_.is_none() { - ("", "", "") - } else { - ("async ", "_async", ".await") - }; + let (instantiate, wait) = ("", ""); self.push_str(&format!( " /// Instantiates the provided `module` using the specified @@ -1095,7 +976,7 @@ impl Generator for Wasmtime { /// The `get_state` parameter is used to access the /// auxiliary state necessary for these wasm exports from /// the general store state `T`. - pub {}fn instantiate( + pub fn instantiate( mut store: impl wasmtime::AsContextMut, module: &wasmtime::Module, linker: &mut wasmtime::Linker, @@ -1106,7 +987,7 @@ impl Generator for Wasmtime { Ok((Self::new(store, &instance,get_state)?, instance)) }} ", - async_fn, name, instantiate, wait, + name, instantiate, wait, )); self.push_str(&format!( @@ -1165,11 +1046,7 @@ impl Generator for Wasmtime { } for r in self.exported_resources.iter() { - let (async_fn, call, wait) = if self.opts.async_.is_none() { - ("", "call", "") - } else { - ("async ", "call_async", ".await") - }; + let (call, wait) = ("call", ""); self.src.push_str(&format!( " /// Drops the host-owned handle to the resource @@ -1179,7 +1056,7 @@ impl Generator for Wasmtime { /// destructor for this type. This also may not run /// the destructor if there are still other references /// to this type. - pub {async}fn drop_{name_snake}( + pub fn drop_{name_snake}( &self, mut store: impl wasmtime::AsContextMut, val: {name_camel}, @@ -1197,7 +1074,6 @@ impl Generator for Wasmtime { name_snake = iface.resources[*r].name.to_snake_case(), name_camel = iface.resources[*r].name.to_camel_case(), idx = r.index(), - async = async_fn, call = call, wait = wait, )); @@ -1261,10 +1137,6 @@ struct FunctionBindgen<'a> { // Whether or not the `caller_memory` variable has been defined and is // available for use. caller_memory_available: bool, - // Whether or not a helper function was called in an async fashion. If so - // and this is an import, then the import must be defined asynchronously as - // well. - async_intrinsic_called: bool, // Code that must be executed before a return, generated during instruction // lowering. cleanup: Option, @@ -1290,7 +1162,6 @@ impl FunctionBindgen<'_> { src: Source::default(), after_call: false, caller_memory_available: false, - async_intrinsic_called: false, tmp: 0, cleanup: None, closures: Source::default(), @@ -1334,12 +1205,7 @@ impl FunctionBindgen<'_> { } fn call_intrinsic(&mut self, name: &str, args: String) { - let (method, suffix) = if self.gen.opts.async_.is_none() { - ("call", "") - } else { - self.async_intrinsic_called = true; - ("call_async", ".await") - }; + let (method, suffix) = ("call", ""); self.push_str(&format!( "func_{}.{}(&mut caller, {}){}?;\n", name, method, args, suffix @@ -2033,28 +1899,18 @@ impl Bindgen for FunctionBindgen<'_> { } self.push_str("self."); self.push_str(&to_rust_ident(name)); - if self.gen.opts.async_.includes(name) { - self.push_str(".call_async("); - } else { - self.push_str(".call("); - } + self.push_str(".call("); self.push_str("&mut caller, ("); for operand in operands { self.push_str(operand); self.push_str(", "); } self.push_str("))"); - if self.gen.opts.async_.includes(name) { - self.push_str(".await"); - } self.push_str("?;\n"); self.after_call = true; self.caller_memory_available = false; // invalidated by call } - Instruction::CallWasmAsyncImport { .. } => unimplemented!(), - Instruction::CallWasmAsyncExport { .. } => unimplemented!(), - Instruction::CallInterface { module: _, func } => { for (i, operand) in operands.iter().enumerate() { self.push_str(&format!("let param{} = {};\n", i, operand)); @@ -2077,9 +1933,6 @@ impl Bindgen for FunctionBindgen<'_> { call.push_str(&format!("param{}, ", i)); } call.push_str(")"); - if self.gen.opts.async_.includes(&func.name) { - call.push_str(".await"); - } self.push_str("let result = "); results.push("result".to_string()); @@ -2145,9 +1998,6 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::ReturnAsyncExport { .. } => unimplemented!(), - Instruction::ReturnAsyncImport { .. } => unimplemented!(), - Instruction::I32Load { offset } => results.push(self.load(*offset, "i32", operands)), Instruction::I32Load8U { offset } => { results.push(format!("i32::from({})", self.load(*offset, "u8", operands))); diff --git a/crates/gen-host-wasmtime-rust/tests/codegen.rs b/crates/gen-host-wasmtime-rust/tests/codegen.rs index ea620024c..9cfdb5d9b 100644 --- a/crates/gen-host-wasmtime-rust/tests/codegen.rs +++ b/crates/gen-host-wasmtime-rust/tests/codegen.rs @@ -38,60 +38,6 @@ mod imports { ); } -mod async_tests { - mod not_async { - wit_bindgen_host_wasmtime_rust::export!({ - src["x"]: "foo: func()", - async: ["bar"], - }); - - struct Me; - - impl x::X for Me { - fn foo(&mut self) {} - } - } - mod one_async { - wit_bindgen_host_wasmtime_rust::export!({ - src["x"]: " - foo: func() -> list - bar: func() - ", - async: ["bar"], - }); - - struct Me; - - #[wit_bindgen_host_wasmtime_rust::async_trait] - impl x::X for Me { - fn foo(&mut self) -> Vec { - Vec::new() - } - - async fn bar(&mut self) {} - } - } - mod one_async_export { - wit_bindgen_host_wasmtime_rust::import!({ - src["x"]: " - foo: func(x: list) - bar: func() - ", - async: ["bar"], - }); - } - mod resource_with_none_async { - wit_bindgen_host_wasmtime_rust::export!({ - src["x"]: " - resource y { - z: func() -> string - } - ", - async: [], - }); - } -} - mod custom_errors { wit_bindgen_host_wasmtime_rust::export!({ src["x"]: " diff --git a/crates/guest-rust/Cargo.toml b/crates/guest-rust/Cargo.toml index 42f460023..25928c321 100644 --- a/crates/guest-rust/Cargo.toml +++ b/crates/guest-rust/Cargo.toml @@ -6,10 +6,8 @@ edition = "2018" [dependencies] wit-bindgen-guest-rust-macro = { path = "../guest-rust-macro", optional = true } -async-trait = { version = "0.1.51", optional = true } bitflags = "1.3" [features] -default = ["macros", "async"] +default = ["macros"] macros = ["wit-bindgen-guest-rust-macro"] -async = ["async-trait"] diff --git a/crates/guest-rust/src/futures.rs b/crates/guest-rust/src/futures.rs deleted file mode 100644 index b06919fa0..000000000 --- a/crates/guest-rust/src/futures.rs +++ /dev/null @@ -1,202 +0,0 @@ -//! Helper library support for `async` wit functions, used for both - -use std::cell::RefCell; -use std::future::Future; -use std::mem; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::*; - -#[cfg(target_arch = "wasm32")] -#[link(wasm_import_module = "canonical_abi")] -extern "C" { - pub fn async_export_done(ctx: i32, ptr: i32); -} - -#[cfg(not(target_arch = "wasm32"))] -pub unsafe extern "C" fn async_export_done(_ctx: i32, _ptr: i32) { - panic!("only supported on wasm"); -} - -struct PollingWaker { - state: RefCell, -} - -enum State { - Waiting(Pin>>), - Polling, - Woken, -} - -// These are valid for single-threaded WebAssembly because everything is -// single-threaded and send/sync don't matter much. This module will need -// an alternative implementation for threaded WebAssembly when that comes about -// to host runtimes off-the-web. -#[cfg(not(target_feature = "atomics"))] -unsafe impl Send for PollingWaker {} -#[cfg(not(target_feature = "atomics"))] -unsafe impl Sync for PollingWaker {} - -/// Runs the `future` provided to completion, polling the future whenever its -/// waker receives a call to `wake`. -pub fn execute(future: impl Future + 'static) { - let waker = Arc::new(PollingWaker { - state: RefCell::new(State::Waiting(Box::pin(future))), - }); - waker.wake() -} - -impl Wake for PollingWaker { - fn wake(self: Arc) { - let mut state = self.state.borrow_mut(); - let mut future = match mem::replace(&mut *state, State::Polling) { - // We are the first wake to come in to wake-up this future. This - // means that we need to actually poll the future, so leave the - // `Polling` state in place. - State::Waiting(future) => future, - - // Otherwise the future is either already polling or it was already - // woken while it was being polled, in both instances we reset the - // state back to `Woken` and then we return. This means that the - // future is owned by some previous stack frame and will drive the - // future as necessary. - State::Polling | State::Woken => { - *state = State::Woken; - return; - } - }; - drop(state); - - // Create the futures waker/context from ourselves, used for polling. - let waker = self.clone().into(); - let mut cx = Context::from_waker(&waker); - loop { - match future.as_mut().poll(&mut cx) { - // The future is finished! By returning here we destroy the - // future and release all of its resources. - Poll::Ready(()) => break, - - // The future has work yet-to-do, so continue below. - Poll::Pending => {} - } - - let mut state = self.state.borrow_mut(); - match *state { - // This means that we were not woken while we were polling and - // the state is as it was when we took out the future before. By - // `Pending` being returned at this point we're guaranteed that - // our waker will be woken up at some point in the future, which - // will come look at this future again. This means that we - // simply store our future and return, since this call to `wake` - // is now finished. - State::Polling => { - *state = State::Waiting(future); - break; - } - - // This means that we received a call to `wake` while we were - // polling. Ideally we'd enqueue some sort of microtask-tick - // here or something like that but for now we just loop around - // and poll again. - State::Woken => {} - - // This shouldn't be possible since we own the future, and no - // one else should insert another future here. - State::Waiting(_) => unreachable!(), - } - } - } -} - -pub struct Oneshot { - inner: Rc>, -} - -pub struct Sender { - inner: Rc>, -} - -struct OneshotInner { - state: RefCell>, -} - -enum OneshotState { - Start, - Waiting(Waker), - Done(T), -} - -impl Oneshot { - /// Returns a new "oneshot" channel as well as a completion callback. - pub fn new() -> (Oneshot, Sender) { - let inner = Rc::new(OneshotInner { - state: RefCell::new(OneshotState::Start), - }); - ( - Oneshot { - inner: Rc::clone(&inner), - }, - Sender { inner }, - ) - } -} - -impl Future for Oneshot { - type Output = T; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut state = self.inner.state.borrow_mut(); - match mem::replace(&mut *state, OneshotState::Start) { - OneshotState::Done(t) => Poll::Ready(t), - OneshotState::Waiting(_) | OneshotState::Start => { - *state = OneshotState::Waiting(cx.waker().clone()); - Poll::Pending - } - } - } -} - -impl Sender { - pub fn into_usize(self) -> usize { - Rc::into_raw(self.inner) as usize - } - - pub unsafe fn from_usize(ptr: usize) -> Sender { - Sender { - inner: Rc::from_raw(ptr as *const _), - } - } - - pub fn send(self, val: T) { - let mut state = self.inner.state.borrow_mut(); - let prev = mem::replace(&mut *state, OneshotState::Done(val)); - // Must `drop` before the `wake` below because waking may induce - // polling which would induce another `borrow_mut` which would - // conflict with this `borrow_mut` otherwise. - drop(state); - - match prev { - // nothing has polled the returned future just yet, so we just - // filled in the result of the computation. Presumably this will - // get picked up at some point in the future. - OneshotState::Start => {} - - // Something was waiting for the result, so we wake the waker - // here which, for wasm, will likely induce polling immediately. - OneshotState::Waiting(waker) => waker.wake(), - - // Shouldn't be possible, this is the only closure that writes - // `Done` and this can only be invoked once. - OneshotState::Done(_) => unreachable!(), - } - } -} - -impl Drop for OneshotInner { - fn drop(&mut self) { - if let OneshotState::Waiting(waker) = &*self.state.borrow() { - waker.wake_by_ref(); - } - } -} diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index 7a5b5c971..6326cfd11 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -6,11 +6,6 @@ use std::ops::Deref; #[cfg(feature = "macros")] pub use wit_bindgen_guest_rust_macro::{export, import}; -#[cfg(feature = "async")] -pub use async_trait::async_trait; -#[cfg(feature = "async")] -mod futures; - // Re-export `bitflags` so that we can reference it from macros. #[doc(hidden)] pub use bitflags; @@ -130,9 +125,6 @@ pub unsafe trait LocalHandle: HandleType { pub mod rt { use std::alloc::{self, Layout}; - #[cfg(feature = "async")] - pub use crate::futures::*; - #[no_mangle] unsafe extern "C" fn canonical_abi_realloc( old_ptr: *mut u8, diff --git a/crates/host-wasmtime-rust-macro/src/lib.rs b/crates/host-wasmtime-rust-macro/src/lib.rs index a97189f1f..c8e0eefdc 100644 --- a/crates/host-wasmtime-rust-macro/src/lib.rs +++ b/crates/host-wasmtime-rust-macro/src/lib.rs @@ -5,7 +5,6 @@ use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{token, Token}; use wit_bindgen_core::{wit_parser::Interface, Direction, Files, Generator}; -use wit_bindgen_gen_host_wasmtime_rust::Async; /// Generate code to support consuming the given interfaces, importaing them /// from wasm modules. @@ -80,7 +79,6 @@ impl Parse for Opts { for field in fields.into_pairs() { match field.into_value() { ConfigField::Interfaces(v) => interfaces = v, - ConfigField::Async(v) => opts.async_ = v, ConfigField::CustomError(v) => opts.custom_error = v, } } @@ -115,7 +113,6 @@ impl Parse for Opts { enum ConfigField { Interfaces(Vec), - Async(wit_bindgen_gen_host_wasmtime_rust::Async), CustomError(bool), } @@ -148,24 +145,6 @@ impl Parse for ConfigField { interfaces.push(interface); } Ok(ConfigField::Interfaces(interfaces)) - } else if l.peek(token::Async) { - if !cfg!(feature = "async") { - return Err( - input.error("async support not enabled in the `wit-bindgen-wasmtime` crate") - ); - } - input.parse::()?; - input.parse::()?; - let val = if input.parse::>()?.is_some() { - Async::All - } else { - let names; - syn::bracketed!(names in input); - let paths = Punctuated::::parse_terminated(&names)?; - let values = paths.iter().map(|s| s.value()).collect(); - Async::Only(values) - }; - Ok(ConfigField::Async(val)) } else if l.peek(kw::custom_error) { input.parse::()?; input.parse::()?; diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 7c4d2665c..c12d18bcd 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -77,7 +77,6 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { let mut methods = Vec::new(); let mut resources = BTreeMap::new(); - let mut async_trait = quote::quote!(); for f in iface.functions.iter() { let name = quote::format_ident!("{}", f.item_name().to_snake_case()); let mut params = f @@ -91,14 +90,8 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { params.remove(0); self_ = quote::quote!(&self,); } - let async_ = if f.is_async { - async_trait = quote::quote!(#[wit_bindgen_guest_rust::async_trait(?Send)]); - quote::quote!(async) - } else { - quote::quote!() - }; let method = quote::quote! { - #async_ fn #name(#self_ #(_: #params),*) -> #ret { + fn #name(#self_ #(_: #params),*) -> #ret { loop {} } }; @@ -115,7 +108,6 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { ret.extend(quote::quote! { struct #camel; - #async_trait impl #snake::#camel for #camel { #(#methods)* } @@ -123,7 +115,6 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { for (id, methods) in resources { let name = quote::format_ident!("{}", iface.resources[id].name.to_camel_case()); ret.extend(quote::quote! { - #async_trait impl #snake::#name for #name { #(#methods)* } @@ -225,15 +216,6 @@ pub fn codegen_wasmtime_export(input: TokenStream) -> TokenStream { }, |_| quote::quote!(), ), - ( - "export-async", - || { - let mut opts = wit_bindgen_gen_host_wasmtime_rust::Opts::default(); - opts.async_ = wit_bindgen_gen_host_wasmtime_rust::Async::All; - opts.build() - }, - |_| quote::quote!(), - ), ], ) } @@ -244,22 +226,11 @@ pub fn codegen_wasmtime_import(input: TokenStream) -> TokenStream { gen_rust( input, Direction::Import, - &[ - ( - "import", - || wit_bindgen_gen_host_wasmtime_rust::Opts::default().build(), - |_| quote::quote!(), - ), - ( - "import-async", - || { - let mut opts = wit_bindgen_gen_host_wasmtime_rust::Opts::default(); - opts.async_ = wit_bindgen_gen_host_wasmtime_rust::Async::All; - opts.build() - }, - |_| quote::quote!(), - ), - ], + &[( + "import", + || wit_bindgen_gen_host_wasmtime_rust::Opts::default().build(), + |_| quote::quote!(), + )], ) } diff --git a/crates/test-rust-wasm/Cargo.toml b/crates/test-rust-wasm/Cargo.toml index 133ebf71b..7b7c10669 100644 --- a/crates/test-rust-wasm/Cargo.toml +++ b/crates/test-rust-wasm/Cargo.toml @@ -48,10 +48,6 @@ test = false name = "invalid" test = false -[[bin]] -name = "async_functions" -test = false - [[bin]] name = "many_arguments" test = false diff --git a/crates/test-rust-wasm/src/bin/async_functions.rs b/crates/test-rust-wasm/src/bin/async_functions.rs deleted file mode 100644 index 6e4f29e75..000000000 --- a/crates/test-rust-wasm/src/bin/async_functions.rs +++ /dev/null @@ -1,3 +0,0 @@ -include!("../../../../tests/runtime/async_functions/wasm.rs"); - -fn main() {} diff --git a/crates/wit-bindgen-demo/demo.wit b/crates/wit-bindgen-demo/demo.wit index e26991488..9381aa0f0 100644 --- a/crates/wit-bindgen-demo/demo.wit +++ b/crates/wit-bindgen-demo/demo.wit @@ -1,11 +1,5 @@ type files = list> -variant wasmtime-async { - all, - none, - only(list), -} - enum lang { js, rust, @@ -24,6 +18,5 @@ resource config { set-rust-unchecked: func(unchecked: bool) set-wasmtime-tracing: func(unchecked: bool) - set-wasmtime-async: func(val: wasmtime-async) set-wasmtime-custom-error: func(custom: bool) } diff --git a/crates/wit-bindgen-demo/index.html b/crates/wit-bindgen-demo/index.html index de9383436..77abe7e36 100644 --- a/crates/wit-bindgen-demo/index.html +++ b/crates/wit-bindgen-demo/index.html @@ -95,11 +95,6 @@

Generated bindings

· - - - - · - diff --git a/crates/wit-bindgen-demo/main.ts b/crates/wit-bindgen-demo/main.ts index bc6c0d48f..a2c5cce54 100644 --- a/crates/wit-bindgen-demo/main.ts +++ b/crates/wit-bindgen-demo/main.ts @@ -8,7 +8,6 @@ class Editor { files: HTMLSelectElement rustUnchecked: HTMLInputElement; wasmtimeTracing: HTMLInputElement; - wasmtimeAsync: HTMLInputElement; wasmtimeCustomError: HTMLInputElement; generatedFiles: Record; demo: Demo; @@ -25,7 +24,6 @@ class Editor { this.files = document.getElementById('file-select') as HTMLSelectElement; this.rustUnchecked = document.getElementById('rust-unchecked') as HTMLInputElement; this.wasmtimeTracing = document.getElementById('wasmtime-tracing') as HTMLInputElement; - this.wasmtimeAsync = document.getElementById('wasmtime-async') as HTMLInputElement; this.wasmtimeCustomError = document.getElementById('wasmtime-custom-error') as HTMLInputElement; this.outputHtml = document.getElementById('html-output') as HTMLDivElement; @@ -76,15 +74,6 @@ class Editor { this.config.setWasmtimeTracing(this.wasmtimeTracing.checked); this.render(); }); - this.wasmtimeAsync.addEventListener('change', () => { - let async_; - if (this.wasmtimeAsync.checked) - async_ = { tag: 'all' }; - else - async_ = { tag: 'none' }; - this.config.setWasmtimeAsync(async_); - this.render(); - }); this.wasmtimeCustomError.addEventListener('change', () => { this.config.setWasmtimeCustomError(this.wasmtimeCustomError.checked); this.render(); diff --git a/crates/wit-bindgen-demo/src/lib.rs b/crates/wit-bindgen-demo/src/lib.rs index d0a8b5e8d..fe3a8cbfb 100644 --- a/crates/wit-bindgen-demo/src/lib.rs +++ b/crates/wit-bindgen-demo/src/lib.rs @@ -89,13 +89,4 @@ impl demo::Config for Config { browser::log("custom error"); self.wasmtime.borrow_mut().custom_error = custom_error; } - fn set_wasmtime_async(&self, async_: demo::WasmtimeAsync) { - use wit_bindgen_gen_host_wasmtime_rust::Async; - - self.wasmtime.borrow_mut().async_ = match async_ { - demo::WasmtimeAsync::All => Async::All, - demo::WasmtimeAsync::None => Async::None, - demo::WasmtimeAsync::Only(list) => Async::Only(list.into_iter().collect()), - }; - } } diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs index b2ec0f47d..4918c657c 100644 --- a/crates/wit-component/src/decoding.rs +++ b/crates/wit-component/src/decoding.rs @@ -249,7 +249,6 @@ impl<'a> InterfaceDecoder<'a> { let result = self.decode_type(&ty.result)?; self.interface.functions.push(Function { - is_async: false, docs: Docs::default(), name: func_name.to_string(), kind: FunctionKind::Freestanding, diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 3930997cf..4c341f8df 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -782,13 +782,6 @@ impl<'a> TypeEncoder<'a> { ); } - if function.is_async { - bail!( - "unsupported function `{}`: only synchronous functions are currently supported", - function.name - ); - } - Ok(()) } } diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs index ae2811b8a..b1c5fda30 100644 --- a/crates/wit-parser/src/abi.rs +++ b/crates/wit-parser/src/abi.rs @@ -3,7 +3,6 @@ use crate::{ Enum, Expected, Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Tuple, Type, TypeDefKind, TypeId, Union, Variant, }; -use std::mem; /// A raw WebAssembly signature with params and results. #[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] @@ -635,60 +634,12 @@ def_instruction! { /// Represents a call to a raw WebAssembly API. The module/name are /// provided inline as well as the types if necessary. - /// - /// Note that this instruction is not currently used for async - /// functions, instead `CallWasmAsyncImport` and `CallWasmAsyncExport` - /// are used. CallWasm { iface: &'a Interface, name: &'a str, sig: &'a WasmSignature, } : [sig.params.len()] => [sig.results.len()], - /// Represents a call to an asynchronous wasm import. - /// - /// This currently only happens when a compiled-to-wasm module calls as - /// async import. This instruction is used to indicate that the - /// specified import function should be called. The specified import - /// function has `params` as its types, but the final two parameters - /// must be synthesized by this instruction which are the - /// callback/callback state. The actual imported function does not - /// return anything but the callback will be called with the `i32` state - /// as the first parameter and `results` as the rest of the parameters. - /// The callback function should return nothing. - /// - /// It's up to the bindings generator to figure out how to make this - /// look synchronous despite it being callback-based in the middle. - CallWasmAsyncImport { - iface: &'a Interface, - name: &'a str, - params: &'a [WasmType], - results: &'a [WasmType], - } : [params.len() - 2] => [results.len()], - - /// Represents a call to an asynchronous wasm export. - /// - /// This currently only happens when a host module calls an async - /// function on a wasm module. The specified function will take `params` - /// as its argument plus one more argument of an `i32` state that the - /// host needs to synthesize. The function being called doesn't actually - /// return anything. Instead wasm will call an `async_export_done` - /// intrinsic in the `canonical_abi` module. This intrinsic receives a - /// context value and a pointer into linear memory. The context value - /// lines up with the final `i32` parameter of this function call (which - /// the bindings generator must synthesize) and the pointer into linear - /// memory contains the `results`, stored at 8-byte offsets in the same - /// manner that multiple results are transferred. - /// - /// It's up to the bindings generator to figure out how to make this - /// look synchronous despite it being callback-based in the middle. - CallWasmAsyncExport { - module: &'a str, - name: &'a str, - params: &'a [WasmType], - results: &'a [WasmType], - } : [params.len() - 1] => [results.len()], - /// Same as `CallWasm`, except the dual where an interface is being /// called rather than a raw wasm function. /// @@ -700,39 +651,8 @@ def_instruction! { /// Returns `amt` values on the stack. This is always the last /// instruction. - /// - /// Note that this instruction is used for asynchronous functions where - /// the results are *lifted*, not when they're *lowered*, though. For - /// those modes the `ReturnAsyncExport` and `ReturnAsyncImport` - /// functions are used. Return { amt: usize, func: &'a Function } : [*amt] => [0], - /// "Returns" from an asynchronous export. - /// - /// This is only used for compiled-to-wasm modules at this time, and - /// only for the exports of async functions in those modules. This - /// instruction receives two parameters, the first of which is the - /// original context from the start of the function which was provided - /// when the export was first called (its last parameter). The second - /// argument is a pointer into linear memory with the results of the - /// asynchronous call already encoded. This instruction should then call - /// the `async_export_done` intrinsic in the `canonical_abi` module. - ReturnAsyncExport { func: &'a Function } : [2] => [0], - - /// "Returns" from an asynchronous import. - /// - /// This is only used for host modules at this time, and - /// only for the import of async functions in those modules. This - /// instruction receives the operands used to call the completion - /// function in the wasm module. The first parameter to this instruction - /// is the index into the function table of the function to call, and - /// the remaining parameters are the parameters to invoke the function - /// with. - ReturnAsyncImport { - func: &'a Function, - params: usize, - } : [*params + 2] => [0], - /// Calls the `realloc` function specified in a malloc-like fashion /// allocating `size` bytes with alignment `align`. /// @@ -906,44 +826,20 @@ impl Interface { self.push_wasm(variant, &func.result, &mut results); let mut retptr = false; - if func.is_async { - // Asynchronous functions never actually return anything since - // they're all callback-based, meaning that we always put all the - // results into a return pointer. - // - // Asynchronous exports take one extra parameter which is the - // context used to pass to the `async_export_done` intrinsic, and - // asynchronous imports take two extra parameters where the first is - // a pointer into the function table and the second is a context - // argument to pass to this function. + + // Rust/C don't support multi-value well right now, so if a function + // would have multiple results then instead truncate it. Imports take a + // return pointer to write into and exports return a pointer they wrote + // into. + if results.len() > MAX_FLAT_RESULTS { + retptr = true; + results.truncate(0); match variant { - AbiVariant::GuestExport => { - retptr = true; - results.truncate(0); - params.push(WasmType::I32); - } AbiVariant::GuestImport => { - retptr = true; - results.truncate(0); - params.push(WasmType::I32); params.push(WasmType::I32); } - } - } else { - // Rust/C don't support multi-value well right now, so if a function - // would have multiple results then instead truncate it. Imports take a - // return pointer to write into and exports return a pointer they wrote - // into. - if results.len() > MAX_FLAT_RESULTS { - retptr = true; - results.truncate(0); - match variant { - AbiVariant::GuestImport => { - params.push(WasmType::I32); - } - AbiVariant::GuestExport => { - results.push(WasmType::I32); - } + AbiVariant::GuestExport => { + results.push(WasmType::I32); } } } @@ -1169,83 +1065,49 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.stack.push(ptr); } - if func.is_async { - // We emit custom instructions for async calls since they - // have different parameters synthesized by the bindings - // generator depending on what kind of call is being made. - // - // Note that no return pointer goop happens here because - // that's all done through parameters of callbacks instead. - let mut results = Vec::new(); - self.iface - .push_wasm(self.variant, &func.result, &mut results); - match self.variant { - AbiVariant::GuestImport => { - assert_eq!(self.stack.len(), sig.params.len() - 2); - self.emit(&Instruction::CallWasmAsyncImport { - iface: self.iface, - name: &func.name, - params: &sig.params, - results: &results, - }); - } - AbiVariant::GuestExport => { - assert_eq!(self.stack.len(), sig.params.len() - 1); - self.emit(&Instruction::CallWasmAsyncExport { - module: &self.iface.name, - name: &func.name, - params: &sig.params, - results: &results, - }); - } - } + // If necessary we may need to prepare a return pointer for + // this ABI. + if self.variant == AbiVariant::GuestImport && sig.retptr { + let size = self.bindgen.sizes().size(&func.result); + let align = self.bindgen.sizes().align(&func.result); + let ptr = self.bindgen.return_pointer(self.iface, 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 { + iface: self.iface, + name: &func.name, + sig: &sig, + }); + if !sig.retptr { + // With no return pointer in use we can simply lift the + // result of the function from the result of the core + // wasm function. self.lift(&func.result); } else { - // If necessary we may need to prepare a return pointer for - // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let size = self.bindgen.sizes().size(&func.result); - let align = self.bindgen.sizes().align(&func.result); - let ptr = self.bindgen.return_pointer(self.iface, 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 { - iface: self.iface, - name: &func.name, - sig: &sig, - }); - - if !sig.retptr { - // With no return pointer in use we can simply lift the - // result of the function from the result of the core - // wasm function. - self.lift(&func.result); - } else { - 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 - // `self.return_pointer`) so we use that to read - // the result of the function from memory. - AbiVariant::GuestImport => { - assert!(sig.results.len() == 0); - self.return_pointer.take().unwrap() - } + 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 + // `self.return_pointer`) so we use that to read + // the result of the function from memory. + AbiVariant::GuestImport => { + assert!(sig.results.len() == 0); + self.return_pointer.take().unwrap() + } - // guest exports means that this is a host - // calling wasm so wasm returned a pointer to where - // the result is stored - AbiVariant::GuestExport => self.stack.pop().unwrap(), - }; + // guest exports means that this is a host + // calling wasm so wasm returned a pointer to where + // the result is stored + AbiVariant::GuestExport => self.stack.pop().unwrap(), + }; - self.read_from_memory(&func.result, ptr, 0); - } + self.read_from_memory(&func.result, ptr, 0); } self.emit(&Instruction::Return { func, amt: 1 }); @@ -1303,95 +1165,45 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if func.is_async { + if !sig.retptr { + // With no return pointer in use we simply lower the + // result and return that directly from the function. + self.lower(&func.result); + } else { match self.variant { - // Returning from a guest import means that the - // completion callback needs to be called which is - // currently given the lowered representation of the - // result. + // When a function is imported to a guest this means + // it's a host providing the implementation of the + // import. The result is stored in the pointer + // specified in the last argument, so we get the + // pointer here and then write the return value into + // it. AbiVariant::GuestImport => { - self.lower(&func.result); - - let mut tys = Vec::new(); - self.iface.push_wasm(self.variant, &func.result, &mut tys); - assert_eq!(self.stack.len(), tys.len()); - let operands = mem::take(&mut self.stack); - // function index to call - self.emit(&Instruction::GetArg { - nth: sig.params.len() - 2, - }); - // environment for the function self.emit(&Instruction::GetArg { nth: sig.params.len() - 1, }); - self.stack.extend(operands); - self.emit(&Instruction::ReturnAsyncImport { - func, - params: tys.len(), - }); + let ptr = self.stack.pop().unwrap(); + self.write_to_memory(&func.result, ptr, 0); } - // Returning from a guest export means that we need to - // invoke the completion intrinsics with where the - // result is stored in linear memory. + // For a guest import this is a function defined in + // wasm, so we're returning a pointer where the + // value was stored at. Allocate some space here + // (statically) and then write the result into that + // memory, returning the pointer at the end. AbiVariant::GuestExport => { let size = self.bindgen.sizes().size(&func.result); let align = self.bindgen.sizes().align(&func.result); let ptr = self.bindgen.return_pointer(self.iface, size, align); self.write_to_memory(&func.result, ptr.clone(), 0); - - // Get the caller's context index. - self.emit(&Instruction::GetArg { - nth: sig.params.len() - 1, - }); self.stack.push(ptr); - - // This will call the "done" function with the - // context/pointer argument - self.emit(&Instruction::ReturnAsyncExport { func }); - } - } - } else { - if !sig.retptr { - // With no return pointer in use we simply lower the - // result and return that directly from the function. - self.lower(&func.result); - } else { - match self.variant { - // When a function is imported to a guest this means - // it's a host providing the implementation of the - // import. The result is stored in the pointer - // specified in the last argument, so we get the - // pointer here and then write the return value into - // it. - AbiVariant::GuestImport => { - self.emit(&Instruction::GetArg { - nth: sig.params.len() - 1, - }); - let ptr = self.stack.pop().unwrap(); - self.write_to_memory(&func.result, ptr, 0); - } - - // For a guest import this is a function defined in - // wasm, so we're returning a pointer where the - // value was stored at. Allocate some space here - // (statically) and then write the result into that - // memory, returning the pointer at the end. - AbiVariant::GuestExport => { - let size = self.bindgen.sizes().size(&func.result); - let align = self.bindgen.sizes().align(&func.result); - let ptr = self.bindgen.return_pointer(self.iface, size, align); - self.write_to_memory(&func.result, ptr.clone(), 0); - self.stack.push(ptr); - } } } - - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); } + + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); } } diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index 411a88f9d..185ca0adf 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -171,7 +171,6 @@ struct UnionCase<'a> { enum ValueKind<'a> { Function { - is_async: bool, params: Vec<(Id<'a>, Type<'a>)>, result: Type<'a>, }, @@ -409,16 +408,13 @@ impl<'a> Value<'a> { tokens.expect(Token::Colon)?; let kind = if tokens.eat(Token::Func)? { - parse_func(tokens, false)? - } else if tokens.eat(Token::Async)? { - tokens.expect(Token::Func)?; - parse_func(tokens, true)? + parse_func(tokens)? } else { ValueKind::Global(Type::parse(tokens)?) }; return Ok(Value { docs, name, kind }); - fn parse_func<'a>(tokens: &mut Tokenizer<'a>, is_async: bool) -> Result> { + fn parse_func<'a>(tokens: &mut Tokenizer<'a>) -> Result> { let params = parse_list( tokens, Token::LeftParen, @@ -435,11 +431,7 @@ impl<'a> Value<'a> { } else { Type::Unit }; - Ok(ValueKind::Function { - is_async, - params, - result, - }) + Ok(ValueKind::Function { params, result }) } } } diff --git a/crates/wit-parser/src/ast/lex.rs b/crates/wit-parser/src/ast/lex.rs index f91c497da..3db757870 100644 --- a/crates/wit-parser/src/ast/lex.rs +++ b/crates/wit-parser/src/ast/lex.rs @@ -80,7 +80,6 @@ pub enum Token { Static, Interface, Tuple, - Async, Unit, Implements, @@ -271,7 +270,6 @@ impl<'a> Tokenizer<'a> { "static" => Static, "interface" => Interface, "tuple" => Tuple, - "async" => Async, "unit" => Unit, "implements" => Implements, _ => Id, @@ -540,7 +538,6 @@ impl Token { Static => "keyword `static`", Interface => "keyword `interface`", Tuple => "keyword `tuple`", - Async => "keyword `async`", Unit => "keyword `unit`", Implements => "keyword `implements`", } diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index c157ca8b6..bf701f2f0 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -579,11 +579,7 @@ impl Resolver { fn resolve_value(&mut self, value: &Value<'_>) -> Result<()> { let docs = self.docs(&value.docs); match &value.kind { - ValueKind::Function { - is_async, - params, - result, - } => { + ValueKind::Function { params, result } => { let params = params .iter() .map(|(name, ty)| Ok((name.name.to_string(), self.resolve_type(ty)?))) @@ -595,7 +591,6 @@ impl Resolver { kind: FunctionKind::Freestanding, params, result, - is_async: *is_async, }); } ValueKind::Global(ty) => { @@ -614,12 +609,8 @@ impl Resolver { let mut names = HashSet::new(); let id = self.resource_lookup[&*resource.name.name]; for (statik, value) in resource.values.iter() { - let (is_async, params, result) = match &value.kind { - ValueKind::Function { - is_async, - params, - result, - } => (*is_async, params, result), + let (params, result) = match &value.kind { + ValueKind::Function { params, result } => (params, result), ValueKind::Global(_) => { return Err(Error { span: value.name.span, @@ -654,7 +645,6 @@ impl Resolver { } }; self.functions.push(Function { - is_async, docs, name: format!("{}::{}", resource.name.name, value.name.name), kind, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 8942d00f2..5bdeb88ff 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -253,7 +253,6 @@ pub struct Global { #[derive(Debug, Clone, PartialEq)] pub struct Function { - pub is_async: bool, pub docs: Docs, pub name: String, pub kind: FunctionKind, diff --git a/crates/wit-parser/tests/all.rs b/crates/wit-parser/tests/all.rs index 2e3af492b..e9b72a7c8 100644 --- a/crates/wit-parser/tests/all.rs +++ b/crates/wit-parser/tests/all.rs @@ -218,8 +218,6 @@ fn to_json(i: &Interface) -> String { #[derive(Serialize)] struct Function { name: String, - #[serde(rename = "async", skip_serializing_if = "Option::is_none")] - is_async: Option, params: Vec, result: String, } @@ -255,7 +253,6 @@ fn to_json(i: &Interface) -> String { .iter() .map(|f| Function { name: f.name.clone(), - is_async: if f.is_async { Some(f.is_async) } else { None }, params: f.params.iter().map(|(_, ty)| translate_type(ty)).collect(), result: translate_type(&f.result), }) diff --git a/crates/wit-parser/tests/ui/async.wit b/crates/wit-parser/tests/ui/async.wit deleted file mode 100644 index 70ec80d03..000000000 --- a/crates/wit-parser/tests/ui/async.wit +++ /dev/null @@ -1,9 +0,0 @@ -a: async func() -b: async func(x: s32) -c: async func() -> u32 - -resource y { - a: async func() - b: async func(x: s32) - c: async func() -> u32 -} diff --git a/crates/wit-parser/tests/ui/async.wit.result b/crates/wit-parser/tests/ui/async.wit.result deleted file mode 100644 index 3d97f8df6..000000000 --- a/crates/wit-parser/tests/ui/async.wit.result +++ /dev/null @@ -1,60 +0,0 @@ -{ - "resources": [ - { - "name": "y" - } - ], - "types": [ - { - "idx": 0, - "primitive": "handle-0" - } - ], - "functions": [ - { - "name": "a", - "async": true, - "params": [], - "result": "unit" - }, - { - "name": "b", - "async": true, - "params": [ - "s32" - ], - "result": "unit" - }, - { - "name": "c", - "async": true, - "params": [], - "result": "u32" - }, - { - "name": "y::a", - "async": true, - "params": [ - "handle-0" - ], - "result": "unit" - }, - { - "name": "y::b", - "async": true, - "params": [ - "handle-0", - "s32" - ], - "result": "unit" - }, - { - "name": "y::c", - "async": true, - "params": [ - "handle-0" - ], - "result": "u32" - } - ] -} \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/async.wit b/crates/wit-parser/tests/ui/parse-fail/async.wit deleted file mode 100644 index 6b058013e..000000000 --- a/crates/wit-parser/tests/ui/parse-fail/async.wit +++ /dev/null @@ -1,2 +0,0 @@ -// parse-fail -a: async diff --git a/crates/wit-parser/tests/ui/parse-fail/async1.wit b/crates/wit-parser/tests/ui/parse-fail/async1.wit deleted file mode 100644 index 663585a8f..000000000 --- a/crates/wit-parser/tests/ui/parse-fail/async1.wit +++ /dev/null @@ -1,3 +0,0 @@ -// parse-fail -a: async() - diff --git a/tests/codegen/async-functions.wit b/tests/codegen/async-functions.wit deleted file mode 100644 index 81d02f62f..000000000 --- a/tests/codegen/async-functions.wit +++ /dev/null @@ -1,7 +0,0 @@ -async-no-args: async func() -async-args: async func(a: u32, b: string, c: list) -async-results: async func() -> tuple> - -resource async-resource { - frob: async func() -} diff --git a/tests/runtime/async_functions/exports.wit b/tests/runtime/async_functions/exports.wit deleted file mode 100644 index c1de3e350..000000000 --- a/tests/runtime/async_functions/exports.wit +++ /dev/null @@ -1,4 +0,0 @@ -thunk: async func() -allocated-bytes: func() -> u32 - -test-concurrent: async func() diff --git a/tests/runtime/async_functions/host.ts b/tests/runtime/async_functions/host.ts deleted file mode 100644 index 4381c7f76..000000000 --- a/tests/runtime/async_functions/host.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { addImportsToImports, Imports } from "./imports.js"; -import { Exports } from "./exports.js"; -import { getWasm, addWasiToImports } from "./helpers.js"; -// @ts-ignore -import * as assert from 'assert'; - -function promiseChannel(): [Promise, () => void] { - let resolveCallback = null; - const promise = new Promise((resolve, reject) => resolveCallback = resolve); - // @ts-ignore - return [promise, resolveCallback]; -} - -async function run() { - const importObj = {}; - let hit = false; - - const [concurrentPromise, resolveConcurrent] = promiseChannel(); - const [unblockConcurrent1, resolveUnblockConcurrent1] = promiseChannel(); - const [unblockConcurrent2, resolveUnblockConcurrent2] = promiseChannel(); - const [unblockConcurrent3, resolveUnblockConcurrent3] = promiseChannel(); - - const imports: Imports = { - async thunk() { - if (hit) { - console.log('second time in thunk, throwing an error'); - throw new Error('catch me'); - } else { - console.log('first time in thunk'); - await some_helper(); - console.log('waited on the helper, returning from host thunk'); - hit = true; - } - }, - - async concurrent1(val) { - console.log('wasm called concurrent1'); - assert.equal(val, 1); - resolveUnblockConcurrent1(); - console.log('concurrent1 to reenter back into the host'); - await concurrentPromise; - console.log('concurrent1 returning to wasm'); - return 11; - }, - async concurrent2(val) { - console.log('wasm called concurrent2'); - assert.equal(val, 2); - resolveUnblockConcurrent2(); - console.log('concurrent2 to reenter back into the host'); - await concurrentPromise; - console.log('concurrent2 returning to wasm'); - return 12; - }, - async concurrent3(val) { - console.log('wasm called concurrent3'); - assert.equal(val, 3); - resolveUnblockConcurrent3(); - console.log('concurrent3 to reenter back into the host'); - await concurrentPromise; - console.log('concurrent3 returning to wasm'); - return 13; - }, - }; - let instance: WebAssembly.Instance; - addImportsToImports(importObj, imports, name => instance.exports[name]); - const wasi = addWasiToImports(importObj); - - const wasm = new Exports(); - await wasm.instantiate(getWasm(), importObj); - wasi.start(wasm.instance); - instance = wasm.instance; - - const initBytes = wasm.allocatedBytes(); - console.log("calling initial async function"); - await wasm.thunk(); - assert.ok(hit, "import not called"); - assert.equal(initBytes, wasm.allocatedBytes()); - - // Make sure that exceptions on the host make their way back to whomever's - // doing the actual `await` - try { - console.log('executing thunk export a second time'); - await wasm.thunk(); - throw new Error('expected an error to get thrown'); - } catch (e) { - const err = e as Error; - console.log('caught error with', err.message); - assert.equal(err.message, 'catch me'); - } - - console.log('entering wasm'); - const concurrentWasm = wasm.testConcurrent(); - console.log('waiting for wasm to enter the host'); - await unblockConcurrent1; - await unblockConcurrent2; - await unblockConcurrent3; - console.log('allowing host functions to finish'); - resolveConcurrent(); - console.log('waiting on host functions'); - await concurrentWasm; - console.log('concurrent wasm finished'); -} - -async function some_helper() {} - -await run() diff --git a/tests/runtime/async_functions/imports.wit b/tests/runtime/async_functions/imports.wit deleted file mode 100644 index aefd084e6..000000000 --- a/tests/runtime/async_functions/imports.wit +++ /dev/null @@ -1,5 +0,0 @@ -thunk: async func() - -concurrent1: async func(a: u32) -> u32 -concurrent2: async func(a: u32) -> u32 -concurrent3: async func(a: u32) -> u32 diff --git a/tests/runtime/async_functions/wasm.rs b/tests/runtime/async_functions/wasm.rs deleted file mode 100644 index 1aa5d9b10..000000000 --- a/tests/runtime/async_functions/wasm.rs +++ /dev/null @@ -1,23 +0,0 @@ -wit_bindgen_guest_rust::import!("../../tests/runtime/async_functions/imports.wit"); -wit_bindgen_guest_rust::export!("../../tests/runtime/async_functions/exports.wit"); - -struct Exports; - -#[wit_bindgen_guest_rust::async_trait(?Send)] -impl exports::Exports for Exports { - fn allocated_bytes() -> u32 { - test_rust_wasm::get() as u32 - } - - async fn thunk() { - imports::thunk().await; - } - - async fn test_concurrent() { - let a1 = imports::concurrent1(1); - let a2 = imports::concurrent2(2); - let a3 = imports::concurrent3(3); - - assert_eq!(futures_util::join!(a2, a3, a1), (12, 13, 11)); - } -}