Files
wasmtime/crates/wiggle/generate/src/wasmtime.rs
2021-07-15 11:44:58 -07:00

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
},
)?;
}
}
}
}