diff --git a/derive/src/codegen.rs b/derive/src/codegen.rs index 59fb7cd..497a7aa 100644 --- a/derive/src/codegen.rs +++ b/derive/src/codegen.rs @@ -20,36 +20,45 @@ pub fn codegen(ext_def: &ExtDefinition, to: &mut TokenStream) { extern crate wasmi as _wasmi; use _wasmi::{ - Trap, RuntimeValue, RuntimeArgs, Externals, - derive_support::WasmResult, + Trap, RuntimeValue, RuntimeArgs, Externals, ValueType, ModuleImportResolver, + Signature, FuncRef, Error, FuncInstance, + derive_support::{ + WasmResult, + ConvertibleToWasm, + }, }; + #[inline(always)] + fn materialize_arg_ty(_w: Option) -> ValueType { + W::VALUE_TYPE + } + + #[inline(always)] + fn materialize_ret_type(_w: Option) -> Option { + W::VALUE_TYPE + } + #externals #module_resolver }; }).to_tokens(to); } -fn gen_dispatch_func_arm(func: &ExternalFunc) -> TokenStream { +fn emit_dispatch_func_arm(func: &ExternalFunc) -> TokenStream { let index = func.index as usize; - let name = Ident::new(&func.name, Span::call_site()); let return_ty_span = func.return_ty.clone().unwrap_or_else(|| Span::call_site()); - let mut args = vec![]; let mut unmarshall_args = TokenStream::new(); - for (i, arg_span) in func.args.iter().cloned().enumerate() { - let mut arg_name = "arg".to_string(); - arg_name.push_str(&i.to_string()); - let arg_name = Ident::new(&arg_name, arg_span.clone()); + for (i, param) in func.args.iter().enumerate() { + let param_span = param.ident.span(); + let ident = ¶m.ident; - (quote_spanned! {arg_span=> - let #arg_name = + (quote_spanned! {param_span=> + let #ident = args.next() .and_then(|rt_val| rt_val.try_into()) .unwrap(); }).to_tokens(&mut unmarshall_args); - - args.push(quote_spanned! {arg_span=> #arg_name }); } let prologue = quote! { @@ -60,21 +69,20 @@ fn gen_dispatch_func_arm(func: &ExternalFunc) -> TokenStream { WasmResult::to_wasm_result(r) }; + let call = { + let args = func.args.iter().map(|param| param.ident.clone()); + let name = Ident::new(&func.name, Span::call_site()); + quote! { + #name( #(#args),* ) + } + }; (quote! { #index => { #prologue - let r = self.#name( #(#args),* ); + let r = self.#call; #epilogue } }) - - // let body = $crate::wasm_utils::constrain_closure::< - // <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _ - // >(|| { - // unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) - // }); - // let r = body()?; - // return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) } fn derive_externals(ext_def: &ExtDefinition, to: &mut TokenStream) { @@ -83,7 +91,7 @@ fn derive_externals(ext_def: &ExtDefinition, to: &mut TokenStream) { let mut match_arms = vec![]; for func in &ext_def.funcs { - match_arms.push(gen_dispatch_func_arm(func)); + match_arms.push(emit_dispatch_func_arm(func)); } (quote::quote! { @@ -104,21 +112,95 @@ fn derive_externals(ext_def: &ExtDefinition, to: &mut TokenStream) { }).to_tokens(to); } -fn derive_module_resolver(ext_def: &ExtDefinition, to: &mut TokenStream) { - (quote::quote! { - impl #impl_generics ModuleImportResolver for #ty #where_clause { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - match index { - #(#match_arms),* - _ => panic!("fn with index {} is undefined", index), - } +fn emit_resolve_func_arm(func: &ExternalFunc) -> TokenStream { + let index = func.index as usize; + let string_ident = &func.name; + let return_ty_span = func.return_ty.clone().unwrap_or_else(|| Span::call_site()); + + let call = { + let args = func.args.iter().map(|param| { + let ident = param.ident.clone(); + let span = param.ident.span(); + quote_spanned! {span=> #ident.unwrap() } + }); + let name = Ident::new(&func.name, Span::call_site()); + quote! { + Self::#name( panic!(), #(#args),* ) + } + }; + + let init = func.args.iter().map(|param| { + let ident = ¶m.ident; + quote! { + let #ident = None; + } + }).collect::>(); + + let params_materialized_tys = func.args.iter().map(|param| { + let ident = ¶m.ident; + let span = param.ident.span(); + quote_spanned! {span=> materialize_arg_ty(#ident) } + }).collect::>(); + + let materialized_return_ty = quote_spanned! { return_ty_span=> + materialize_ret_type(return_val) + }; + + quote! { + if name == #string_ident { + // initialize variables + #(#init)* + + #[allow(unreachable_code)] + let return_val = if false { + // calling self for typeinference + Some(#call) + } else { + None + }; + + // at this point types of all variables and return_val are inferred. + if signature.params() != &[#(#params_materialized_tys),*] || signature.return_type() != #materialized_return_ty { + return Err(Error::Instantiation( + format!("Export {} has different signature {:?}", #string_ident, signature), + )); } - // ... + return Ok(FuncInstance::alloc_host(signature.clone(), #index)); + } + } +} + +fn derive_module_resolver(ext_def: &ExtDefinition, to: &mut TokenStream) { + let (impl_generics, ty_generics, where_clause) = ext_def.generics.split_for_impl(); + let ty = &ext_def.ty; + + let mut match_arms = vec![]; + for func in &ext_def.funcs { + match_arms.push(emit_resolve_func_arm(func)); + } + + (quote::quote! { + impl #impl_generics #ty #where_clause { + fn resolver() -> impl ModuleImportResolver { + // Use a closure to have an ability to use `Self` type + let resolve_func = |name: &str, signature: &Signature| -> Result { + #(#match_arms)* + + Err(Error::Instantiation( + format!("Export {} not found", name), + )) + }; + + struct Resolver(fn(&str, &Signature) -> Result); + impl ModuleImportResolver for Resolver { + #[inline(always)] + fn resolve_func(&self, name: &str, signature: &Signature) -> Result { + (self.0)(name, signature) + } + } + Resolver(resolve_func) + } } }).to_tokens(to); } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index eca13e2..7e8e5b3 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit="128"] + extern crate proc_macro; mod model; @@ -28,7 +30,7 @@ pub fn derive_externals(attr: TokenStream, input: TokenStream) -> TokenStream { // and we will then need to return both the original implementation and the generated implementation // of externals. - // println!("{:?}", quote::quote! { #input }.to_string()); + println!("{:?}", quote::quote! { #input }.to_string()); let input = input.into(); input } diff --git a/derive/src/model.rs b/derive/src/model.rs index acd2481..834b596 100644 --- a/derive/src/model.rs +++ b/derive/src/model.rs @@ -10,9 +10,10 @@ pub struct Signature { pub return_ty: Option, } +#[derive(Clone)] pub struct Param { - span: proc_macro2::Span, - generated_name: String, + /// A generated identifier used for temporary variables. + pub ident: syn::Ident, } pub struct ExternalFunc { diff --git a/derive/src/parser.rs b/derive/src/parser.rs index a38d17e..55f2702 100644 --- a/derive/src/parser.rs +++ b/derive/src/parser.rs @@ -1,5 +1,5 @@ -use crate::model::{self, ExtDefinition, ExternalFunc}; -use syn::{ItemImpl, ImplItem, ImplItemMethod, FnArg, ReturnType}; +use crate::model::{self, ExtDefinition, ExternalFunc, Param}; +use syn::{ItemImpl, ImplItem, ImplItemMethod, FnArg, ReturnType, Ident}; use syn::spanned::Spanned; /// Parse an incoming stream of tokens into a list of external functions. @@ -17,8 +17,12 @@ pub fn parse(input: proc_macro2::TokenStream) -> Result { let index = funcs.len() as u32; // self TODO: handle this properly - let args = sig.decl.inputs.iter().skip(1).enumerate().map(|input| { - input.span() + let args = sig.decl.inputs.iter().skip(1).enumerate().map(|(idx, input)| { + let param_name = format!("arg{}", idx); + let ident = Ident::new(¶m_name, input.span()); + Param { + ident, + } }).collect::>(); let return_ty = match sig.decl.output { diff --git a/src/derive_support.rs b/src/derive_support.rs index 830fbe1..d4d88e1 100644 --- a/src/derive_support.rs +++ b/src/derive_support.rs @@ -16,16 +16,19 @@ impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } pub trait WasmResult { + const VALUE_TYPE: Option; fn to_wasm_result(self) -> Result, Trap>; } impl WasmResult for () { + const VALUE_TYPE: Option = None; fn to_wasm_result(self) -> Result, Trap> { Ok(None) } } impl> WasmResult for Result { + const VALUE_TYPE: Option = Some(R::VALUE_TYPE); fn to_wasm_result(self) -> Result, Trap> { self .map(|v| Some(v.to_runtime_value())) @@ -34,6 +37,7 @@ impl> WasmResult for Result { } impl> WasmResult for Result<(), E> { + const VALUE_TYPE: Option = None; fn to_wasm_result(self) -> Result, Trap> { self .map(|_| None) @@ -42,6 +46,7 @@ impl> WasmResult for Result<(), E> { } impl WasmResult for R { + const VALUE_TYPE: Option = Some(R::VALUE_TYPE); fn to_wasm_result(self) -> Result, Trap> { Ok(Some(self.to_runtime_value())) } diff --git a/tests/derives.rs b/tests/derives.rs index 82fb150..0db2aab 100644 --- a/tests/derives.rs +++ b/tests/derives.rs @@ -31,10 +31,4 @@ impl<'a> NonStaticExternals<'a> { pub fn traps(&self) -> Result<(), NoInfoError> { Err(NoInfoError) } - - pub fn fart(&self, inbound_fart: Fart) -> Result { - Ok(inbound_fart) - } } - -pub struct Fart;