This is currently a very common operation in host bindings where if wasm gives a host function a relative pointer you'll want to simulataneously work with the host state and the wasm memory. These two regions are distinct and safe to borrow mutably simulataneously but it's not obvious in the Rust type system that this is so, so add a helper method here to assist in doing so.
171 lines
5.1 KiB
Rust
171 lines
5.1 KiB
Rust
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<T, U>(
|
|
linker: &mut #rt::wasmtime_crate::Linker<T>,
|
|
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::<Vec<_>>();
|
|
let arg_decls = params
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, ty)| {
|
|
let name = &arg_names[i];
|
|
let wasm = names.wasm_type(*ty);
|
|
quote! { #name: #wasm }
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
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
|
|
},
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
}
|