use heck::*; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::io::{Read, Write}; use std::mem; use std::process::{Command, Stdio}; use std::str::FromStr; use wit_bindgen_gen_core::wit_parser::abi::{ AbiVariant, Bindgen, Instruction, LiftLower, WasmType, }; use wit_bindgen_gen_core::{wit_parser::*, Direction, Files, Generator, Source, TypeInfo, Types}; use wit_bindgen_gen_rust::{ to_rust_ident, wasm_type, FnSig, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode, }; #[derive(Default)] pub struct Wasmtime { src: Source, opts: Opts, needs_get_memory: bool, needs_get_func: bool, needs_char_from_i32: bool, needs_invalid_variant: bool, needs_validate_flags: bool, needs_raw_mem: bool, needs_bad_int: bool, needs_copy_slice: bool, needs_buffer_glue: bool, needs_le: bool, needs_custom_error_to_trap: bool, needs_custom_error_to_types: BTreeSet, all_needed_handles: BTreeSet, exported_resources: BTreeSet, types: Types, guest_imports: HashMap>, guest_exports: HashMap, in_import: bool, in_trait: bool, trait_name: String, sizes: SizeAlign, } enum NeededFunction { Realloc, Free, } struct Import { is_async: bool, name: String, trait_signature: String, num_wasm_params: usize, closure: String, } #[derive(Default)] struct Exports { fields: BTreeMap, funcs: Vec, } #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "structopt", derive(structopt::StructOpt))] pub struct Opts { /// Whether or not `rustfmt` is executed to format generated code. #[cfg_attr(feature = "structopt", structopt(long))] pub rustfmt: bool, /// Whether or not to emit `tracing` macro calls on function entry/exit. #[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(); r.opts = self; r } } enum FunctionRet { /// The function return is normal and needs to extra handling. Normal, /// The function return was wrapped in a `Result` in Rust. The `Ok` variant /// is the actual value that will be lowered, and the `Err`, if present, /// means that a trap has occurred. CustomToTrap, /// The function returns a `Result` in both wasm and in Rust, but the /// Rust error type is a custom error and must be converted to `err`. The /// `ok` variant payload is provided here too. CustomToError { ok: Type, err: String }, } impl Wasmtime { pub fn new() -> Wasmtime { Wasmtime::default() } fn abi_variant(dir: Direction) -> AbiVariant { // This generator uses a reversed mapping! In the Wasmtime host-side // bindings, we don't use any extra adapter layer between guest wasm // modules and the host. When the guest imports functions using the // `GuestImport` ABI, the host directly implements the `GuestImport` // ABI, even though the host is *exporting* functions. Similarly, when // the guest exports functions using the `GuestExport` ABI, the host // directly imports them with the `GuestExport` ABI, even though the // host is *importing* functions. match dir { Direction::Import => AbiVariant::GuestExport, Direction::Export => AbiVariant::GuestImport, } } fn print_intrinsics(&mut self) { if self.needs_raw_mem { self.push_str("use wit_bindgen_wasmtime::rt::RawMem;\n"); } if self.needs_char_from_i32 { self.push_str("use wit_bindgen_wasmtime::rt::char_from_i32;\n"); } if self.needs_invalid_variant { self.push_str("use wit_bindgen_wasmtime::rt::invalid_variant;\n"); } if self.needs_bad_int { self.push_str("use core::convert::TryFrom;\n"); self.push_str("use wit_bindgen_wasmtime::rt::bad_int;\n"); } if self.needs_validate_flags { self.push_str("use wit_bindgen_wasmtime::rt::validate_flags;\n"); } if self.needs_le { self.push_str("use wit_bindgen_wasmtime::Le;\n"); } if self.needs_copy_slice { self.push_str("use wit_bindgen_wasmtime::rt::copy_slice;\n"); } } /// Classifies the return value of a function to see if it needs handling /// with respect to the `custom_error` configuration option. fn classify_fn_ret(&mut self, iface: &Interface, f: &Function) -> FunctionRet { if !self.opts.custom_error { return FunctionRet::Normal; } if let Type::Id(id) = &f.result { if let TypeDefKind::Expected(e) = &iface.types[*id].kind { if let Type::Id(err) = e.err { if let Some(name) = &iface.types[err].name { self.needs_custom_error_to_types.insert(name.clone()); return FunctionRet::CustomToError { ok: e.ok, err: name.to_string(), }; } } } } self.needs_custom_error_to_trap = true; FunctionRet::CustomToTrap } } impl RustGenerator for Wasmtime { fn default_param_mode(&self) -> TypeMode { if self.in_import { // The default here is that only leaf values can be borrowed because // otherwise lists and such need to be copied into our own memory. TypeMode::LeafBorrowed("'a") } else { // When we're calling wasm exports, however, there's no need to take // any ownership of anything from the host so everything is borrowed // in the parameter position. TypeMode::AllBorrowed("'a") } } fn handle_projection(&self) -> Option<(&'static str, String)> { if self.in_import { if self.in_trait { Some(("Self", self.trait_name.clone())) } else { Some(("T", self.trait_name.clone())) } } else { None } } fn handle_wrapper(&self) -> Option<&'static str> { None } fn push_str(&mut self, s: &str) { self.src.push_str(s); } fn info(&self, ty: TypeId) -> TypeInfo { self.types.get(ty) } fn types_mut(&mut self) -> &mut Types { &mut self.types } fn print_borrowed_slice( &mut self, iface: &Interface, mutbl: bool, ty: &Type, lifetime: &'static str, ) { if self.sizes.align(ty) > 1 && self.in_import { // If we're generating bindings for an import we ideally want to // hand out raw pointers into memory. We can't guarantee anything // about alignment in memory, though, so if the alignment // requirement is bigger than one then we have to use slices where // the type has a `Le<...>` wrapper. // // For exports we're generating functions that take values from // Rust, so we can assume alignment and use raw slices. For types // with an align of 1, then raw pointers are fine since Rust will // have the same alignment requirement. self.needs_le = true; self.push_str("&"); if lifetime != "'_" { self.push_str(lifetime); self.push_str(" "); } if mutbl { self.push_str(" mut "); } self.push_str("[Le<"); self.print_ty(iface, ty, TypeMode::AllBorrowed(lifetime)); self.push_str(">]"); } else { self.print_rust_slice(iface, mutbl, ty, lifetime); } } fn print_borrowed_str(&mut self, lifetime: &'static str) { self.push_str("&"); if lifetime != "'_" { self.push_str(lifetime); self.push_str(" "); } self.push_str(" str"); } } impl Generator for Wasmtime { fn preprocess_one(&mut self, iface: &Interface, dir: Direction) { let variant = Self::abi_variant(dir); self.types.analyze(iface); self.in_import = variant == AbiVariant::GuestImport; self.trait_name = iface.name.to_camel_case(); self.src.push_str(&format!( "#[allow(clippy::all)]\npub mod {} {{\n", iface.name.to_snake_case(), )); self.src .push_str("#[allow(unused_imports)]\nuse wit_bindgen_wasmtime::{wasmtime, anyhow};\n"); self.sizes.fill(iface); } fn type_record( &mut self, iface: &Interface, id: TypeId, name: &str, record: &Record, docs: &Docs, ) { self.print_typedef_record(iface, id, record, docs); // If this record might be used as a slice type in various places then // we synthesize an `Endian` implementation for it so `&[Le]` // is usable. if self.modes_of(iface, id).len() > 0 && record.fields.iter().all(|f| iface.all_bits_valid(&f.ty)) { self.src.push_str("impl wit_bindgen_wasmtime::Endian for "); self.src.push_str(&name.to_camel_case()); self.src.push_str(" {\n"); self.src.push_str("fn into_le(self) -> Self {\n"); self.src.push_str("Self {\n"); for field in record.fields.iter() { self.src.push_str(&field.name.to_snake_case()); self.src.push_str(": self."); self.src.push_str(&field.name.to_snake_case()); self.src.push_str(".into_le(),\n"); } self.src.push_str("}\n"); self.src.push_str("}\n"); self.src.push_str("fn from_le(self) -> Self {\n"); self.src.push_str("Self {\n"); for field in record.fields.iter() { self.src.push_str(&field.name.to_snake_case()); self.src.push_str(": self."); self.src.push_str(&field.name.to_snake_case()); self.src.push_str(".from_le(),\n"); } self.src.push_str("}\n"); self.src.push_str("}\n"); self.src.push_str("}\n"); // Also add an `AllBytesValid` valid impl since this structure's // byte representations are valid (guarded by the `all_bits_valid` // predicate). self.src .push_str("unsafe impl wit_bindgen_wasmtime::AllBytesValid for "); self.src.push_str(&name.to_camel_case()); self.src.push_str(" {}\n"); } } fn type_tuple( &mut self, iface: &Interface, id: TypeId, _name: &str, tuple: &Tuple, docs: &Docs, ) { self.print_typedef_tuple(iface, id, tuple, docs); } fn type_flags( &mut self, _iface: &Interface, _id: TypeId, name: &str, flags: &Flags, docs: &Docs, ) { self.src .push_str("wit_bindgen_wasmtime::bitflags::bitflags! {\n"); self.rustdoc(docs); let repr = RustFlagsRepr::new(flags); self.src .push_str(&format!("pub struct {}: {repr} {{\n", name.to_camel_case())); for (i, flag) in flags.flags.iter().enumerate() { self.rustdoc(&flag.docs); self.src.push_str(&format!( "const {} = 1 << {};\n", flag.name.to_shouty_snake_case(), i, )); } self.src.push_str("}\n"); self.src.push_str("}\n\n"); self.src.push_str("impl core::fmt::Display for "); self.src.push_str(&name.to_camel_case()); self.src.push_str( "{\nfn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n", ); self.src.push_str("f.write_str(\""); self.src.push_str(&name.to_camel_case()); self.src.push_str("(\")?;\n"); self.src.push_str("core::fmt::Debug::fmt(self, f)?;\n"); self.src.push_str("f.write_str(\" (0x\")?;\n"); self.src .push_str("core::fmt::LowerHex::fmt(&self.bits, f)?;\n"); self.src.push_str("f.write_str(\"))\")?;\n"); self.src.push_str("Ok(())"); self.src.push_str("}\n"); self.src.push_str("}\n\n"); } fn type_variant( &mut self, iface: &Interface, id: TypeId, _name: &str, variant: &Variant, docs: &Docs, ) { self.print_typedef_variant(iface, id, variant, docs); } fn type_union( &mut self, iface: &Interface, id: TypeId, _name: &str, union: &Union, docs: &Docs, ) { self.print_typedef_union(iface, id, union, docs); } fn type_option( &mut self, iface: &Interface, id: TypeId, _name: &str, payload: &Type, docs: &Docs, ) { self.print_typedef_option(iface, id, payload, docs); } fn type_expected( &mut self, iface: &Interface, id: TypeId, _name: &str, expected: &Expected, docs: &Docs, ) { self.print_typedef_expected(iface, id, expected, docs); } fn type_enum(&mut self, _iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { self.print_typedef_enum(id, name, enum_, docs); } fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { let name = &iface.resources[ty].name; self.all_needed_handles.insert(name.to_string()); // If we're binding imports then all handles are associated types so // there's nothing that we need to do about that. if self.in_import { return; } self.exported_resources.insert(ty); // ... otherwise for exports we generate a newtype wrapper around an // `i32` to manage the resultt. let tyname = name.to_camel_case(); self.rustdoc(&iface.resources[ty].docs); self.src.push_str("#[derive(Debug)]\n"); self.src.push_str(&format!( "pub struct {}(wit_bindgen_wasmtime::rt::ResourceIndex);\n", tyname )); } fn type_alias(&mut self, iface: &Interface, id: TypeId, _name: &str, ty: &Type, docs: &Docs) { self.print_typedef_alias(iface, id, ty, docs); } fn type_list(&mut self, iface: &Interface, id: TypeId, _name: &str, ty: &Type, docs: &Docs) { self.print_type_list(iface, id, ty, docs); } fn type_builtin(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { self.rustdoc(docs); self.src .push_str(&format!("pub type {}", name.to_camel_case())); self.src.push_str(" = "); self.print_ty(iface, ty, TypeMode::Owned); self.src.push_str(";\n"); } // As with `abi_variant` above, we're generating host-side bindings here // 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 // codegen here. let sig = iface.wasm_signature(AbiVariant::GuestImport, func); let params = (0..sig.params.len()) .map(|i| format!("arg{}", i)) .collect::>(); let mut f = FunctionBindgen::new(self, params); iface.call( AbiVariant::GuestImport, LiftLower::LiftArgsLowerResults, func, &mut f, ); let FunctionBindgen { src, cleanup, needs_borrow_checker, needs_memory, needs_buffer_transaction, needs_functions, closures, async_intrinsic_called, .. } = f; assert!(cleanup.is_none()); assert!(!needs_buffer_transaction); // Generate the signature this function will have in the final trait let self_arg = "&mut self".to_string(); self.in_trait = true; 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 // the `custom_error` configuration of this code generator. match self.classify_fn_ret(iface, func) { FunctionRet::Normal => { self.push_str(" -> "); self.print_ty(iface, &func.result, TypeMode::Owned); } FunctionRet::CustomToTrap => { self.push_str(" -> Result<"); self.print_ty(iface, &func.result, TypeMode::Owned); self.push_str(", Self::Error>"); } FunctionRet::CustomToError { ok, .. } => { self.push_str(" -> Result<"); self.print_ty(iface, &ok, TypeMode::Owned); self.push_str(", Self::Error>"); } } self.in_trait = false; let trait_signature = mem::take(&mut self.src).into(); // Generate the closure that's passed to a `Linker`, the final piece of // codegen here. self.src .push_str("move |mut caller: wasmtime::Caller<'_, T>"); for (i, param) in sig.params.iter().enumerate() { let arg = format!("arg{}", i); self.src.push_str(","); self.src.push_str(&arg); self.src.push_str(":"); self.wasm_type(*param); } 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!( " let span = wit_bindgen_wasmtime::tracing::span!( wit_bindgen_wasmtime::tracing::Level::TRACE, \"wit-bindgen abi\", module = \"{}\", function = \"{}\", ); let _enter = span.enter(); ", iface.name, func.name, )); } self.src.push_str(&closures); for (name, func) in needs_functions { self.src.push_str(&format!( " let func = get_func(&mut caller, \"{name}\")?; let func_{name} = func.typed::<{cvt}, _>(&caller)?; ", name = name, cvt = func.cvt(), )); self.needs_get_func = true; } if needs_memory || needs_borrow_checker { self.src .push_str("let memory = &get_memory(&mut caller, \"memory\")?;\n"); self.needs_get_memory = true; } if needs_borrow_checker { self.src.push_str( "let (mem, data) = memory.data_and_store_mut(&mut caller); let mut _bc = wit_bindgen_wasmtime::BorrowChecker::new(mem); let host = get(data);\n", ); } else { self.src.push_str("let host = get(caller.data_mut());\n"); } if self.all_needed_handles.len() > 0 { self.src.push_str("let (host, _tables) = host;\n"); } 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(); self.guest_imports .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, }); } // As with `abi_variant` above, we're generating host-side bindings here // 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<"); self.print_ty(iface, &func.result, TypeMode::Owned); self.push_str(", wasmtime::Trap> {\n"); let params = func .params .iter() .map(|(name, _)| to_rust_ident(name).to_string()) .collect(); let mut f = FunctionBindgen::new(self, params); iface.call( AbiVariant::GuestExport, LiftLower::LowerArgsLiftResults, func, &mut f, ); let FunctionBindgen { needs_memory, src, needs_borrow_checker, needs_buffer_transaction, closures, needs_functions, .. } = f; let exports = self .guest_exports .entry(iface.name.to_string()) .or_insert_with(Exports::default); for (name, func) in needs_functions { self.src .push_str(&format!("let func_{0} = &self.{0};\n", name)); let get = format!( "instance.get_typed_func::<{}, _>(&mut store, \"{}\")?", func.cvt(), name ); exports.fields.insert(name, (func.ty(), get)); } self.src.push_str(&closures); assert!(!needs_borrow_checker); if needs_memory { self.src.push_str("let memory = &self.memory;\n"); exports.fields.insert( "memory".to_string(), ( "wasmtime::Memory".to_string(), "instance .get_memory(&mut store, \"memory\") .ok_or_else(|| { anyhow::anyhow!(\"`memory` export not a memory\") })? " .to_string(), ), ); } if needs_buffer_transaction { self.needs_buffer_glue = true; self.src .push_str("let mut buffer_transaction = self.buffer_glue.transaction();\n"); } self.src.push_str(&String::from(src)); self.src.push_str("}\n"); let func_body = mem::replace(&mut self.src, prev); exports.funcs.push(func_body.into()); // Create the code snippet which will define the type of this field in // the struct that we're exporting and additionally extracts the // function from an instantiated instance. let sig = iface.wasm_signature(AbiVariant::GuestExport, func); let mut cvt = "(".to_string(); for param in sig.params.iter() { cvt.push_str(wasm_type(*param)); cvt.push_str(","); } cvt.push_str("), ("); for result in sig.results.iter() { cvt.push_str(wasm_type(*result)); cvt.push_str(","); } cvt.push_str(")"); exports.fields.insert( to_rust_ident(&func.name), ( format!("wasmtime::TypedFunc<{}>", cvt), format!( "instance.get_typed_func::<{}, _>(&mut store, \"{}\")?", cvt, func.name, ), ), ); } 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_wasmtime::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"); } } if self.opts.custom_error { self.src.push_str("type Error;\n"); if self.needs_custom_error_to_trap { self.src.push_str( "fn error_to_trap(&mut self, err: Self::Error) -> wasmtime::Trap;\n", ); } for ty in self.needs_custom_error_to_types.iter() { self.src.push_str(&format!( "fn error_to_{}(&mut self, err: Self::Error) -> Result<{}, wasmtime::Trap>;\n", ty.to_snake_case(), ty.to_camel_case(), )); } } for f in funcs { self.src.push_str(&f.trait_signature); self.src.push_str(";\n\n"); } for handle in self.all_needed_handles.iter() { self.src.push_str(&format!( "fn drop_{}(&mut self, state: Self::{}) {{ drop(state); }}\n", handle.to_snake_case(), handle.to_camel_case(), )); } self.src.push_str("}\n"); if self.all_needed_handles.len() > 0 { self.src.push_str("\npub struct "); self.src.push_str(&module_camel); self.src.push_str("Tables {\n"); for handle in self.all_needed_handles.iter() { self.src.push_str("pub(crate) "); self.src.push_str(&handle.to_snake_case()); self.src.push_str("_table: wit_bindgen_wasmtime::Table,\n"); } self.src.push_str("}\n"); self.src.push_str("impl Default for "); self.src.push_str(&module_camel); self.src.push_str("Tables {\n"); self.src.push_str("fn default() -> Self { Self {"); for handle in self.all_needed_handles.iter() { self.src.push_str(&handle.to_snake_case()); self.src.push_str("_table: Default::default(),"); } self.src.push_str("}}}"); } } 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() { self.push_str("&mut U"); } else { self.push_str(&format!("(&mut U, &mut {}Tables)", module_camel)); } 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_wasmtime::rt::get_memory;\n"); } if self.needs_get_func { self.push_str("use wit_bindgen_wasmtime::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") }; self.push_str(&format!( "linker.{}(\"{}\", \"{}\", {})?;\n", method, module, f.name, f.closure, )); } for handle in self.all_needed_handles.iter() { self.src.push_str(&format!( "linker.func_wrap( \"canonical_abi\", \"resource_drop_{name}\", move |mut caller: wasmtime::Caller<'_, T>, handle: u32| {{ let (host, tables) = get(caller.data_mut()); let handle = tables .{snake}_table .remove(handle) .map_err(|e| {{ wasmtime::Trap::new(format!(\"failed to remove handle: {{}}\", e)) }})?; host.drop_{snake}(handle); Ok(()) }} )?;\n", name = handle, snake = handle.to_snake_case(), )); } self.push_str("Ok(())\n}\n"); } for (module, exports) in sorted_iter(&mem::take(&mut self.guest_exports)) { let name = module.to_camel_case(); // Generate a struct that is the "state" of this exported module // which is required to be included in the host state `T` of the // store. self.push_str( " /// Auxiliary data associated with the wasm exports. /// /// This is required to be stored within the data of a /// `Store` itself so lifting/lowering state can be managed /// when translating between the host and wasm. ", ); self.push_str("#[derive(Default)]\n"); self.push_str("pub struct "); self.push_str(&name); self.push_str("Data {\n"); for r in self.exported_resources.iter() { self.src.push_str(&format!( " index_slab{}: wit_bindgen_wasmtime::rt::IndexSlab, resource_slab{0}: wit_bindgen_wasmtime::rt::ResourceSlab, dtor{0}: Option>, ", r.index() )); } self.push_str("}\n"); self.push_str("pub struct "); self.push_str(&name); self.push_str(" {\n"); self.push_str(&format!( "get_state: Box &mut {}Data + Send + Sync>,\n", name )); for (name, (ty, _)) in exports.fields.iter() { self.push_str(name); self.push_str(": "); self.push_str(ty); self.push_str(",\n"); } self.push_str("}\n"); let bound = if self.opts.async_.is_none() { "" } else { ": Send" }; self.push_str(&format!("impl {} {{\n", bound, name)); if self.exported_resources.len() == 0 { self.push_str("#[allow(unused_variables)]\n"); } self.push_str(&format!( " /// Adds any intrinsics, if necessary for this exported wasm /// functionality to the `linker` provided. /// /// The `get_state` closure is required to access the /// auxiliary data necessary for these wasm exports from /// the general store's state. pub fn add_to_linker( linker: &mut wasmtime::Linker, get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static, ) -> anyhow::Result<()> {{ ", 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 {", "})", ) }; self.src.push_str(&format!( " linker.{func_wrap}( \"canonical_abi\", \"resource_drop_{name}\", move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {prefix}{{ let state = get_state(caller.data_mut()); let resource_idx = state.index_slab{idx}.remove(idx)?; let wasm = match state.resource_slab{idx}.drop(resource_idx) {{ Some(wasm) => wasm, None => return Ok(()), }}; let dtor = state.dtor{idx}.expect(\"destructor not set yet\"); dtor.{call}(&mut caller, wasm){wait}?; Ok(()) }}{suffix}, )?; linker.func_wrap( \"canonical_abi\", \"resource_clone_{name}\", move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {{ let state = get_state(caller.data_mut()); let resource_idx = state.index_slab{idx}.get(idx)?; state.resource_slab{idx}.clone(resource_idx)?; Ok(state.index_slab{idx}.insert(resource_idx)) }}, )?; linker.func_wrap( \"canonical_abi\", \"resource_get_{name}\", move |mut caller: wasmtime::Caller<'_, T>, idx: u32| {{ let state = get_state(caller.data_mut()); let resource_idx = state.index_slab{idx}.get(idx)?; Ok(state.resource_slab{idx}.get(resource_idx)) }}, )?; linker.func_wrap( \"canonical_abi\", \"resource_new_{name}\", move |mut caller: wasmtime::Caller<'_, T>, val: i32| {{ let state = get_state(caller.data_mut()); let resource_idx = state.resource_slab{idx}.insert(val); Ok(state.index_slab{idx}.insert(resource_idx)) }}, )?; ", name = iface.resources[*r].name, idx = r.index(), func_wrap = func_wrap, call = call, wait = wait, prefix = prefix, suffix = suffix, )); } self.push_str("Ok(())\n"); self.push_str("}\n"); let (async_fn, instantiate, wait) = if self.opts.async_.is_none() { ("", "", "") } else { ("async ", "_async", ".await") }; self.push_str(&format!( " /// Instantiates the provided `module` using the specified /// parameters, wrapping up the result in a structure that /// translates between wasm and the host. /// /// The `linker` provided will have intrinsics added to it /// automatically, so it's not necessary to call /// `add_to_linker` beforehand. This function will /// instantiate the `module` otherwise using `linker`, and /// both an instance of this structure and the underlying /// `wasmtime::Instance` will be returned. /// /// 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( mut store: impl wasmtime::AsContextMut, module: &wasmtime::Module, linker: &mut wasmtime::Linker, get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static, ) -> anyhow::Result<(Self, wasmtime::Instance)> {{ Self::add_to_linker(linker, get_state)?; let instance = linker.instantiate{}(&mut store, module){}?; Ok((Self::new(store, &instance,get_state)?, instance)) }} ", async_fn, name, instantiate, wait, )); self.push_str(&format!( " /// Low-level creation wrapper for wrapping up the exports /// of the `instance` provided in this structure of wasm /// exports. /// /// This function will extract exports from the `instance` /// defined within `store` and wrap them all up in the /// returned structure which can be used to interact with /// the wasm module. pub fn new( mut store: impl wasmtime::AsContextMut, instance: &wasmtime::Instance, get_state: impl Fn(&mut T) -> &mut {}Data + Send + Sync + Copy + 'static, ) -> anyhow::Result {{ ", name, )); self.push_str("let mut store = store.as_context_mut();\n"); assert!(!self.needs_get_func); for (name, (_, get)) in exports.fields.iter() { self.push_str("let "); self.push_str(&name); self.push_str("= "); self.push_str(&get); self.push_str(";\n"); } for r in self.exported_resources.iter() { self.src.push_str(&format!( " get_state(store.data_mut()).dtor{} = \ Some(instance.get_typed_func::(\ &mut store, \ \"canonical_abi_drop_{}\", \ )?);\n ", r.index(), iface.resources[*r].name, )); } self.push_str("Ok("); self.push_str(&name); self.push_str("{\n"); for (name, _) in exports.fields.iter() { self.push_str(name); self.push_str(",\n"); } self.push_str("get_state: Box::new(get_state),\n"); self.push_str("\n})\n"); self.push_str("}\n"); for func in exports.funcs.iter() { self.push_str(func); } for r in self.exported_resources.iter() { let (async_fn, call, wait) = if self.opts.async_.is_none() { ("", "call", "") } else { ("async ", "call_async", ".await") }; self.src.push_str(&format!( " /// Drops the host-owned handle to the resource /// specified. /// /// Note that this may execute the WebAssembly-defined /// 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}( &self, mut store: impl wasmtime::AsContextMut, val: {name_camel}, ) -> Result<(), wasmtime::Trap> {{ let mut store = store.as_context_mut(); let data = (self.get_state)(store.data_mut()); let wasm = match data.resource_slab{idx}.drop(val.0) {{ Some(val) => val, None => return Ok(()), }}; data.dtor{idx}.unwrap().{call}(&mut store, wasm){wait}?; Ok(()) }} ", 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, )); } self.push_str("}\n"); } self.print_intrinsics(); // Close the opening `mod`. self.push_str("}\n"); let mut src = mem::take(&mut self.src); if self.opts.rustfmt { let mut child = Command::new("rustfmt") .arg("--edition=2018") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("failed to spawn `rustfmt`"); child .stdin .take() .unwrap() .write_all(src.as_bytes()) .unwrap(); src.as_mut_string().truncate(0); child .stdout .take() .unwrap() .read_to_string(src.as_mut_string()) .unwrap(); let status = child.wait().unwrap(); assert!(status.success()); } files.push("bindings.rs", src.as_bytes()); } } struct FunctionBindgen<'a> { gen: &'a mut Wasmtime, // Number used to assign unique names to temporary variables. tmp: usize, // Destination where source code is pushed onto for this function src: Source, // The named parameters that are available to this function params: Vec, // Management of block scopes used by `Bindgen`. block_storage: Vec, blocks: Vec, // Whether or not the code generator is after the invocation of wasm or the // host, used for knowing where to acquire memory from. after_call: bool, // 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, // Rust clousures for buffers that must be placed at the front of the // function. closures: Source, // Various intrinsic properties this function's codegen required, must be // satisfied in the function header if any are set. needs_buffer_transaction: bool, needs_borrow_checker: bool, needs_memory: bool, needs_functions: HashMap, } impl FunctionBindgen<'_> { fn new(gen: &mut Wasmtime, params: Vec) -> FunctionBindgen<'_> { FunctionBindgen { gen, block_storage: Vec::new(), blocks: Vec::new(), src: Source::default(), after_call: false, caller_memory_available: false, async_intrinsic_called: false, tmp: 0, cleanup: None, closures: Source::default(), needs_buffer_transaction: false, needs_borrow_checker: false, needs_memory: false, needs_functions: HashMap::new(), params, } } fn memory_src(&mut self) -> String { if self.gen.in_import { if !self.after_call { // Before calls we use `_bc` which is a borrow checker used for // getting long-lasting borrows into memory. self.needs_borrow_checker = true; return format!("_bc"); } if !self.caller_memory_available { self.needs_memory = true; self.caller_memory_available = true; // get separate borrows of `caller_memory` and `_tables` if we // might need handle tables later. If we don't end up using // `_tables` that's ok, it'll almost always be optimized away. if self.gen.all_needed_handles.len() > 0 { self.push_str( "let (caller_memory, data) = memory.data_and_store_mut(&mut caller);\n", ); self.push_str("let (_, _tables) = get(data);\n"); } else { self.push_str("let caller_memory = memory.data_mut(&mut caller);\n"); } } format!("caller_memory") } else { self.needs_memory = true; format!("memory.data_mut(&mut caller)") } } 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") }; self.push_str(&format!( "func_{}.{}(&mut caller, {}){}?;\n", name, method, args, suffix )); self.caller_memory_available = false; // invalidated by call } fn load(&mut self, offset: i32, ty: &str, operands: &[String]) -> String { let mem = self.memory_src(); self.gen.needs_raw_mem = true; let tmp = self.tmp(); self.push_str(&format!( "let load{} = {}.load::<{}>({} + {})?;\n", tmp, mem, ty, operands[0], offset )); format!("load{}", tmp) } fn store(&mut self, offset: i32, method: &str, extra: &str, operands: &[String]) { let mem = self.memory_src(); self.gen.needs_raw_mem = true; self.push_str(&format!( "{}.store({} + {}, wit_bindgen_wasmtime::rt::{}({}){})?;\n", mem, operands[1], offset, method, operands[0], extra )); } } impl RustFunctionGenerator for FunctionBindgen<'_> { fn push_str(&mut self, s: &str) { self.src.push_str(s); } fn tmp(&mut self) -> usize { let ret = self.tmp; self.tmp += 1; ret } fn rust_gen(&self) -> &dyn RustGenerator { self.gen } fn lift_lower(&self) -> LiftLower { if self.gen.in_import { LiftLower::LiftArgsLowerResults } else { LiftLower::LowerArgsLiftResults } } } impl Bindgen for FunctionBindgen<'_> { type Operand = String; fn sizes(&self) -> &SizeAlign { &self.gen.sizes } fn push_block(&mut self) { let prev = mem::take(&mut self.src); self.block_storage.push(prev); } fn finish_block(&mut self, operands: &mut Vec) { let to_restore = self.block_storage.pop().unwrap(); let src = mem::replace(&mut self.src, to_restore); let expr = match operands.len() { 0 => "()".to_string(), 1 => operands[0].clone(), _ => format!("({})", operands.join(", ")), }; if src.is_empty() { self.blocks.push(expr); } else if operands.is_empty() { self.blocks.push(format!("{{\n{}}}", &src[..])); } else { self.blocks.push(format!("{{\n{}{}\n}}", &src[..], expr)); } self.caller_memory_available = false; } fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String { unimplemented!() } fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool { iface.all_bits_valid(ty) } fn emit( &mut self, iface: &Interface, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, ) { let mut top_as = |cvt: &str| { let mut s = operands.pop().unwrap(); s.push_str(" as "); s.push_str(cvt); results.push(s); }; let mut try_from = |cvt: &str, operands: &[String], results: &mut Vec| { self.gen.needs_bad_int = true; let result = format!("{}::try_from({}).map_err(bad_int)?", cvt, operands[0]); results.push(result); }; match inst { Instruction::GetArg { nth } => results.push(self.params[*nth].clone()), Instruction::I32Const { val } => results.push(format!("{}i32", val)), Instruction::ConstZero { tys } => { for ty in tys.iter() { match ty { WasmType::I32 => results.push("0i32".to_string()), WasmType::I64 => results.push("0i64".to_string()), WasmType::F32 => results.push("0.0f32".to_string()), WasmType::F64 => results.push("0.0f64".to_string()), } } } Instruction::I64FromU64 | Instruction::I64FromS64 => { let s = operands.pop().unwrap(); results.push(format!("wit_bindgen_wasmtime::rt::as_i64({})", s)); } Instruction::I32FromChar | Instruction::I32FromU8 | Instruction::I32FromS8 | Instruction::I32FromU16 | Instruction::I32FromS16 | Instruction::I32FromU32 | Instruction::I32FromS32 => { let s = operands.pop().unwrap(); results.push(format!("wit_bindgen_wasmtime::rt::as_i32({})", s)); } Instruction::F32FromFloat32 | Instruction::F64FromFloat64 | Instruction::Float32FromF32 | Instruction::Float64FromF64 | Instruction::S32FromI32 | Instruction::S64FromI64 => { results.push(operands.pop().unwrap()); } // Downcasts from `i32` into smaller integers are checked to ensure // that they fit within the valid range. While not strictly // necessary since we could chop bits off this should be more // forward-compatible with any future changes. Instruction::S8FromI32 => try_from("i8", operands, results), Instruction::U8FromI32 => try_from("u8", operands, results), Instruction::S16FromI32 => try_from("i16", operands, results), Instruction::U16FromI32 => try_from("u16", operands, results), // Casts of the same bit width simply use `as` since we're just // reinterpreting the bits already there. Instruction::U32FromI32 => top_as("u32"), Instruction::U64FromI64 => top_as("u64"), Instruction::CharFromI32 => { self.gen.needs_char_from_i32 = true; results.push(format!("char_from_i32({})?", operands[0])); } Instruction::Bitcasts { casts } => { wit_bindgen_gen_rust::bitcast(casts, operands, results) } Instruction::UnitLower => { self.push_str(&format!("let () = {};\n", operands[0])); } Instruction::UnitLift => { results.push("()".to_string()); } Instruction::I32FromBool => { results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0])); } Instruction::BoolFromI32 => { self.gen.needs_invalid_variant = true; results.push(format!( "match {} {{ 0 => false, 1 => true, _ => return Err(invalid_variant(\"bool\")), }}", operands[0], )); } Instruction::I32FromOwnedHandle { ty } => { let name = &iface.resources[*ty].name; results.push(format!( "_tables.{}_table.insert({}) as i32", name.to_snake_case(), operands[0] )); } Instruction::HandleBorrowedFromI32 { ty } => { let name = &iface.resources[*ty].name; results.push(format!( "_tables.{}_table.get(({}) as u32).ok_or_else(|| {{ wasmtime::Trap::new(\"invalid handle index\") }})?", name.to_snake_case(), operands[0] )); } Instruction::I32FromBorrowedHandle { ty } => { let tmp = self.tmp(); self.push_str(&format!( " let obj{tmp} = {op}; (self.get_state)(caller.as_context_mut().data_mut()).resource_slab{idx}.clone(obj{tmp}.0)?; let handle{tmp} = (self.get_state)(caller.as_context_mut().data_mut()).index_slab{idx}.insert(obj{tmp}.0); ", tmp = tmp, idx = ty.index(), op = operands[0], )); results.push(format!("handle{} as i32", tmp,)); } Instruction::HandleOwnedFromI32 { ty } => { let tmp = self.tmp(); self.push_str(&format!( "let handle{} = (self.get_state)(caller.as_context_mut().data_mut()).index_slab{}.remove({} as u32)?;\n", tmp, ty.index(), operands[0], )); let name = iface.resources[*ty].name.to_camel_case(); results.push(format!("{}(handle{})", name, tmp)); } Instruction::RecordLower { ty, record, .. } => { self.record_lower(iface, *ty, record, &operands[0], results); } Instruction::RecordLift { ty, record, .. } => { self.record_lift(iface, *ty, record, operands, results); } Instruction::TupleLower { tuple, .. } => { self.tuple_lower(tuple, &operands[0], results); } Instruction::TupleLift { .. } => { self.tuple_lift(operands, results); } Instruction::FlagsLower { flags, .. } => { let tmp = self.tmp(); self.push_str(&format!("let flags{} = {};\n", tmp, operands[0])); for i in 0..flags.repr().count() { results.push(format!("(flags{}.bits >> {}) as i32", tmp, i * 32)); } } Instruction::FlagsLift { flags, name, .. } => { self.gen.needs_validate_flags = true; let repr = RustFlagsRepr::new(flags); let mut flags = String::from("0"); for (i, op) in operands.iter().enumerate() { flags.push_str(&format!("| (({} as {repr}) << {})", op, i * 32)); } results.push(format!( "validate_flags( {}, {name}::all().bits(), \"{name}\", |bits| {name} {{ bits }} )?", flags, name = name.to_camel_case(), )); } Instruction::VariantPayloadName => results.push("e".to_string()), Instruction::VariantLower { variant, results: result_types, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); self.let_results(result_types.len(), results); let op0 = &operands[0]; self.push_str(&format!("match {op0} {{\n")); let name = self.typename_lower(iface, *ty); for (case, block) in variant.cases.iter().zip(blocks) { let case_name = case.name.to_camel_case(); self.push_str(&format!("{name}::{case_name}")); if case.ty == Type::Unit { self.push_str(&format!(" => {{\nlet e = ();\n{block}\n}}\n")); } else { self.push_str(&format!("(e) => {block},\n")); } } self.push_str("};\n"); } Instruction::VariantLift { variant, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); let op0 = &operands[0]; let mut result = format!("match {op0} {{\n"); let name = self.typename_lift(iface, *ty); for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() { let block = if case.ty != Type::Unit { format!("({block})") } else { String::new() }; let case = case.name.to_camel_case(); result.push_str(&format!("{i} => {name}::{case}{block},\n")); } result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n")); result.push_str("}"); results.push(result); self.gen.needs_invalid_variant = true; } Instruction::UnionLower { union, results: result_types, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - union.cases.len()..) .collect::>(); self.let_results(result_types.len(), results); let op0 = &operands[0]; self.push_str(&format!("match {op0} {{\n")); let name = self.typename_lower(iface, *ty); for (case_name, block) in self .gen .union_case_names(iface, union) .into_iter() .zip(blocks) { self.push_str(&format!("{name}::{case_name}(e) => {block},\n")); } self.push_str("};\n"); } Instruction::UnionLift { union, ty, .. } => { let blocks = self .blocks .drain(self.blocks.len() - union.cases.len()..) .collect::>(); let op0 = &operands[0]; let mut result = format!("match {op0} {{\n"); let name = self.typename_lift(iface, *ty); for (i, (case_name, block)) in self .gen .union_case_names(iface, union) .into_iter() .zip(blocks) .enumerate() { result.push_str(&format!("{i} => {name}::{case_name}({block}),\n")); } result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n")); result.push_str("}"); results.push(result); } Instruction::OptionLower { results: result_types, .. } => { let some = self.blocks.pop().unwrap(); let none = self.blocks.pop().unwrap(); self.let_results(result_types.len(), results); let operand = &operands[0]; self.push_str(&format!( "match {operand} {{ Some(e) => {some}, None => {{\nlet e = ();\n{none}\n}}, }};" )); } Instruction::OptionLift { .. } => { let some = self.blocks.pop().unwrap(); let none = self.blocks.pop().unwrap(); assert_eq!(none, "()"); let operand = &operands[0]; results.push(format!( "match {operand} {{ 0 => None, 1 => Some({some}), _ => return Err(invalid_variant(\"option\")), }}" )); self.gen.needs_invalid_variant = true; } Instruction::ExpectedLower { results: result_types, .. } => { let err = self.blocks.pop().unwrap(); let ok = self.blocks.pop().unwrap(); self.let_results(result_types.len(), results); let operand = &operands[0]; self.push_str(&format!( "match {operand} {{ Ok(e) => {{ {ok} }}, Err(e) => {{ {err} }}, }};" )); } Instruction::ExpectedLift { .. } => { let err = self.blocks.pop().unwrap(); let ok = self.blocks.pop().unwrap(); let operand = &operands[0]; results.push(format!( "match {operand} {{ 0 => Ok({ok}), 1 => Err({err}), _ => return Err(invalid_variant(\"expected\")), }}" )); self.gen.needs_invalid_variant = true; } Instruction::EnumLower { .. } => { results.push(format!("{} as i32", operands[0])); } Instruction::EnumLift { name, enum_, .. } => { let op0 = &operands[0]; let mut result = format!("match {op0} {{\n"); let name = name.to_camel_case(); for (i, case) in enum_.cases.iter().enumerate() { let case = case.name.to_camel_case(); result.push_str(&format!("{i} => {name}::{case},\n")); } result.push_str(&format!("_ => return Err(invalid_variant(\"{name}\")),\n")); result.push_str("}"); results.push(result); self.gen.needs_invalid_variant = true; } Instruction::ListCanonLower { element, realloc } => { // Lowering only happens when we're passing lists into wasm, // which forces us to always allocate, so this should always be // `Some`. let realloc = realloc.unwrap(); self.needs_functions .insert(realloc.to_string(), NeededFunction::Realloc); let (size, align) = (self.gen.sizes.size(element), self.gen.sizes.align(element)); // Store the operand into a temporary... let tmp = self.tmp(); let val = format!("vec{}", tmp); self.push_str(&format!("let {} = {};\n", val, operands[0])); // ... and then realloc space for the result in the guest module let ptr = format!("ptr{}", tmp); self.push_str(&format!("let {} = ", ptr)); self.call_intrinsic( realloc, format!("(0, 0, {}, ({}.len() as i32) * {})", align, val, size), ); // ... and then copy over the result. let mem = self.memory_src(); self.push_str(&format!("{}.store_many({}, &{})?;\n", mem, ptr, val)); self.gen.needs_raw_mem = true; self.needs_memory = true; results.push(ptr); results.push(format!("{}.len() as i32", val)); } Instruction::ListCanonLift { element, free, .. } => match free { Some(free) => { self.needs_memory = true; self.gen.needs_copy_slice = true; self.needs_functions .insert(free.to_string(), NeededFunction::Free); let (align, el_size) = (self.sizes().align(element), self.sizes().size(element)); let tmp = self.tmp(); self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); self.push_str(&format!( " let data{tmp} = copy_slice( &mut caller, memory, ptr{tmp}, len{tmp}, {} )?; ", align, tmp = tmp, )); self.call_intrinsic( free, // we use normal multiplication here as copy_slice has // already verified that multiplied size fits i32 format!("(ptr{tmp}, len{tmp} * {}, {})", el_size, align, tmp = tmp), ); results.push(format!("data{}", tmp)); } None => { self.needs_borrow_checker = true; let tmp = self.tmp(); self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); let slice = format!("_bc.slice(ptr{0}, len{0})?", tmp); results.push(slice); } }, Instruction::StringLower { realloc } => { // see above for this unwrap let realloc = realloc.unwrap(); self.needs_functions .insert(realloc.to_string(), NeededFunction::Realloc); // Store the operand into a temporary... let tmp = self.tmp(); let val = format!("vec{}", tmp); self.push_str(&format!("let {} = {};\n", val, operands[0])); // ... and then realloc space for the result in the guest module let ptr = format!("ptr{}", tmp); self.push_str(&format!("let {} = ", ptr)); self.call_intrinsic(realloc, format!("(0, 0, 1, {}.len() as i32)", val)); // ... and then copy over the result. let mem = self.memory_src(); self.push_str(&format!( "{}.store_many({}, {}.as_bytes())?;\n", mem, ptr, val )); self.gen.needs_raw_mem = true; self.needs_memory = true; results.push(ptr); results.push(format!("{}.len() as i32", val)); } Instruction::StringLift { free } => match free { Some(free) => { self.needs_memory = true; self.gen.needs_copy_slice = true; self.needs_functions .insert(free.to_string(), NeededFunction::Free); let tmp = self.tmp(); self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); self.push_str(&format!( " let data{tmp} = copy_slice( &mut caller, memory, ptr{tmp}, len{tmp}, 1, )?; ", tmp = tmp, )); self.call_intrinsic( free, // we use normal multiplication here as copy_slice has // already verified that multiplied size fits i32 format!("(ptr{tmp}, len{tmp}, 1)", tmp = tmp), ); results.push(format!( "String::from_utf8(data{}) .map_err(|_| wasmtime::Trap::new(\"invalid utf-8\"))?", tmp, )); } None => { self.needs_borrow_checker = true; let tmp = self.tmp(); self.push_str(&format!("let ptr{} = {};\n", tmp, operands[0])); self.push_str(&format!("let len{} = {};\n", tmp, operands[1])); let slice = format!("_bc.slice_str(ptr{0}, len{0})?", tmp); results.push(slice); } }, Instruction::ListLower { element, realloc } => { let realloc = realloc.unwrap(); let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let vec = format!("vec{}", tmp); let result = format!("result{}", tmp); let len = format!("len{}", tmp); self.needs_functions .insert(realloc.to_string(), NeededFunction::Realloc); let size = self.gen.sizes.size(element); let align = self.gen.sizes.align(element); // first store our vec-to-lower in a temporary since we'll // reference it multiple times. self.push_str(&format!("let {} = {};\n", vec, operands[0])); self.push_str(&format!("let {} = {}.len() as i32;\n", len, vec)); // ... then realloc space for the result in the guest module self.push_str(&format!("let {} = ", result)); self.call_intrinsic(realloc, format!("(0, 0, {}, {} * {})", align, len, size)); // ... then consume the vector and use the block to lower the // result. self.push_str(&format!( "for (i, e) in {}.into_iter().enumerate() {{\n", vec )); self.push_str(&format!("let base = {} + (i as i32) * {};\n", result, size)); self.push_str(&body); self.push_str("}"); results.push(result); results.push(len); } Instruction::ListLift { element, free, .. } => { let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); let align = self.gen.sizes.align(element); let len = format!("len{}", tmp); self.push_str(&format!("let {} = {};\n", len, operands[1])); let base = format!("base{}", tmp); self.push_str(&format!("let {} = {};\n", base, operands[0])); let result = format!("result{}", tmp); self.push_str(&format!( "let mut {} = Vec::with_capacity({} as usize);\n", result, len, )); self.push_str("for i in 0.."); self.push_str(&len); self.push_str(" {\n"); self.push_str("let base = "); self.push_str(&base); self.push_str(" + i *"); self.push_str(&size.to_string()); self.push_str(";\n"); self.push_str(&result); self.push_str(".push("); self.push_str(&body); self.push_str(");\n"); self.push_str("}\n"); results.push(result); if let Some(free) = free { self.call_intrinsic(free, format!("({}, {} * {}, {})", base, len, size, align)); self.needs_functions .insert(free.to_string(), NeededFunction::Free); } } Instruction::IterElem { .. } => { self.caller_memory_available = false; // invalidated by for loop results.push("e".to_string()) } Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { iface: _, name, sig, } => { if sig.results.len() > 0 { let tmp = self.tmp(); self.push_str("let ("); for i in 0..sig.results.len() { let arg = format!("result{}_{}", tmp, i); self.push_str(&arg); self.push_str(","); results.push(arg); } self.push_str(") = "); } 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("&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)); } if self.gen.opts.tracing && func.params.len() > 0 { self.push_str("wit_bindgen_wasmtime::tracing::event!(\n"); self.push_str("wit_bindgen_wasmtime::tracing::Level::TRACE,\n"); for (i, (name, _ty)) in func.params.iter().enumerate() { self.push_str(&format!( "{} = wit_bindgen_wasmtime::tracing::field::debug(¶m{}),\n", to_rust_ident(name), i )); } self.push_str(");\n"); } let mut call = format!("host.{}(", func.name.to_snake_case()); for i in 0..operands.len() { 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()); match self.gen.classify_fn_ret(iface, func) { FunctionRet::Normal => self.push_str(&call), // Unwrap the result, translating errors to unconditional // traps FunctionRet::CustomToTrap => { self.push_str("match "); self.push_str(&call); self.push_str("{\n"); self.push_str("Ok(val) => val,\n"); self.push_str("Err(e) => return Err(host.error_to_trap(e)),\n"); self.push_str("}"); } // Keep the `Result` as a `Result`, but convert the error // to either the expected destination value or a trap, // propagating a trap outwards. FunctionRet::CustomToError { err, .. } => { self.push_str("match "); self.push_str(&call); self.push_str("{\n"); self.push_str("Ok(val) => Ok(val),\n"); self.push_str(&format!( "Err(e) => Err(host.error_to_{}(e)?),\n", err.to_snake_case() )); self.push_str("}"); } } self.push_str(";\n"); self.after_call = true; match &func.result { Type::Unit => {} _ if self.gen.opts.tracing => { self.push_str("wit_bindgen_wasmtime::tracing::event!(\n"); self.push_str("wit_bindgen_wasmtime::tracing::Level::TRACE,\n"); self.push_str(&format!( "{} = wit_bindgen_wasmtime::tracing::field::debug(&{0}),\n", results[0], )); self.push_str(");\n"); } _ => {} } } Instruction::Return { amt, .. } => { let result = match amt { 0 => format!("Ok(())\n"), 1 => format!("Ok({})\n", operands[0]), _ => format!("Ok(({}))\n", operands.join(", ")), }; match self.cleanup.take() { Some(cleanup) => { self.push_str("let ret = "); self.push_str(&result); self.push_str(";\n"); self.push_str(&cleanup); self.push_str("ret"); } None => self.push_str(&result), } } 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))); } Instruction::I32Load8S { offset } => { results.push(format!("i32::from({})", self.load(*offset, "i8", operands))); } Instruction::I32Load16U { offset } => { results.push(format!( "i32::from({})", self.load(*offset, "u16", operands) )); } Instruction::I32Load16S { offset } => { results.push(format!( "i32::from({})", self.load(*offset, "i16", operands) )); } Instruction::I64Load { offset } => results.push(self.load(*offset, "i64", operands)), Instruction::F32Load { offset } => results.push(self.load(*offset, "f32", operands)), Instruction::F64Load { offset } => results.push(self.load(*offset, "f64", operands)), Instruction::I32Store { offset } => self.store(*offset, "as_i32", "", operands), Instruction::I64Store { offset } => self.store(*offset, "as_i64", "", operands), Instruction::F32Store { offset } => self.store(*offset, "as_f32", "", operands), Instruction::F64Store { offset } => self.store(*offset, "as_f64", "", operands), Instruction::I32Store8 { offset } => self.store(*offset, "as_i32", " as u8", operands), Instruction::I32Store16 { offset } => { self.store(*offset, "as_i32", " as u16", operands) } Instruction::Malloc { realloc, size, align, } => { self.needs_functions .insert(realloc.to_string(), NeededFunction::Realloc); let tmp = self.tmp(); let ptr = format!("ptr{}", tmp); self.push_str(&format!("let {} = ", ptr)); self.call_intrinsic(realloc, format!("(0, 0, {}, {})", align, size)); results.push(ptr); } Instruction::Free { .. } => unimplemented!(), } } } impl NeededFunction { fn cvt(&self) -> &'static str { match self { NeededFunction::Realloc => "(i32, i32, i32, i32), i32", NeededFunction::Free => "(i32, i32, i32), ()", } } fn ty(&self) -> String { format!("wasmtime::TypedFunc<{}>", self.cvt()) } } fn sorted_iter(map: &HashMap) -> impl Iterator { let mut list = map.into_iter().collect::>(); list.sort_by_key(|p| p.0); list.into_iter() }