use crate::config::Asyncness; use crate::funcs::func_bounds; use crate::{CodegenSettings, Names}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashSet; pub fn link_module( module: &witx::Module, names: &Names, target_path: Option<&syn::Path>, settings: &CodegenSettings, ) -> TokenStream { let module_ident = names.module(&module.name); let send_bound = if settings.async_.contains_async(module) { quote! { + Send, T: Send } } else { quote! {} }; let mut bodies = Vec::new(); let mut bounds = HashSet::new(); for f in module.funcs() { let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str()); bodies.push(generate_func(&module, &f, names, target_path, asyncness)); let bound = func_bounds(names, module, &f, settings); for b in bound { bounds.insert(b); } } let ctx_bound = if let Some(target_path) = target_path { let bounds = bounds .into_iter() .map(|b| quote!(#target_path::#module_ident::#b)); quote!( #(#bounds)+* #send_bound ) } else { let bounds = bounds.into_iter(); quote!( #(#bounds)+* #send_bound ) }; let func_name = if target_path.is_none() { format_ident!("add_to_linker") } else { format_ident!("add_{}_to_linker", module_ident) }; let rt = names.runtime_mod(); quote! { /// Adds all instance items to the specified `Linker`. pub fn #func_name( linker: &mut #rt::wasmtime_crate::Linker, get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, ) -> #rt::anyhow::Result<()> where U: #ctx_bound #send_bound { #(#bodies)* Ok(()) } } } fn generate_func( module: &witx::Module, func: &witx::InterfaceFunc, names: &Names, target_path: Option<&syn::Path>, asyncness: Asyncness, ) -> TokenStream { let rt = names.runtime_mod(); let module_str = module.name.as_str(); let module_ident = names.module(&module.name); let field_str = func.name.as_str(); let field_ident = names.func(&func.name); let (params, results) = func.wasm_signature(); let arg_names = (0..params.len()) .map(|i| Ident::new(&format!("arg{}", i), Span::call_site())) .collect::>(); let arg_decls = params .iter() .enumerate() .map(|(i, ty)| { let name = &arg_names[i]; let wasm = names.wasm_type(*ty); quote! { #name: #wasm } }) .collect::>(); let ret_ty = match results.len() { 0 => quote!(()), 1 => names.wasm_type(results[0]), _ => unimplemented!(), }; let await_ = if asyncness.is_sync() { quote!() } else { quote!(.await) }; let abi_func = if let Some(target_path) = target_path { quote!( #target_path::#module_ident::#field_ident ) } else { quote!( #field_ident ) }; let body = quote! { let mem = match caller.get_export("memory") { Some(#rt::wasmtime_crate::Extern::Memory(m)) => m, _ => { return Err(#rt::wasmtime_crate::Trap::new("missing required memory export")); } }; let (mem , ctx) = mem.data_and_store_mut(&mut caller); let ctx = get_cx(ctx); let mem = #rt::wasmtime::WasmtimeGuestMemory::new(mem); match #abi_func(ctx, &mem #(, #arg_names)*) #await_ { Ok(r) => Ok(<#ret_ty>::from(r)), Err(#rt::Trap::String(err)) => Err(#rt::wasmtime_crate::Trap::new(err)), Err(#rt::Trap::I32Exit(err)) => Err(#rt::wasmtime_crate::Trap::i32_exit(err)), } }; match asyncness { Asyncness::Async => { let wrapper = format_ident!("func_wrap{}_async", params.len()); quote! { linker.#wrapper( #module_str, #field_str, move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| { Box::new(async move { #body }) }, )?; } } Asyncness::Blocking => { quote! { linker.func_wrap( #module_str, #field_str, move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> { let result = async { #body }; #rt::run_in_dummy_executor(result)? }, )?; } } Asyncness::Sync => { quote! { linker.func_wrap( #module_str, #field_str, move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> Result<#ret_ty, #rt::wasmtime_crate::Trap> { #body }, )?; } } } }