diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index bfe720f2d9..255e7a90e9 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use crate::names::Names; @@ -6,10 +6,6 @@ use crate::names::Names; // FIXME need to template what argument is required to an import function - some context // struct (e.g. WasiCtx) should be provided at the invocation of the `gen` proc macro. // -// Additionally - need to template how to transform GuestValueError and MemoryError into -// the error type returned! From impl is a good start, but what if we want to log -// a more informative error? Maybe the conversion should be a (generated, for each by-value first -// return type) trait impled by WasiCtx? pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let ident = names.func(&func.name); @@ -38,7 +34,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .chain(func.results.iter().skip(1)) .map(arg_signature); let abi_args = quote!( - wasi_ctx: &mut WasiCtx, memory: GuestMemory, + ctx: &mut WasiCtx, memory: ::memory::GuestMemory, #(#params),* ); let abi_ret = if let Some(first_result) = func.results.get(0) { @@ -47,10 +43,70 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { _ => unreachable!("first result should always be passed by value"), } } else if func.noreturn { - quote!(!) + // Ideally we would return `quote!(!)` here, but, we'd have to change + // the error handling logic in all the marshalling code to never return, + // and instead provide some other way to bail to the context... + // noreturn func + unimplemented!("noreturn funcs not supported yet!") } else { quote!(()) }; - quote!(pub fn #ident(#abi_args) -> #abi_ret { unimplemented!() }) + let err_type = func + .results + .get(0) + .map(|res| names.type_ref(&res.tref)) + .unwrap_or_else(|| abi_ret.clone()); + let err_val = func + .results + .get(0) + .map(|_res| quote!(#abi_ret::from(e))) + .unwrap_or_else(|| quote!(())); + + let marshal_args = func + .params + .iter() + .map(|param| match param.tref.type_().passed_by() { + witx::TypePassedBy::Value(_atom) => { + // FIXME atom -> param.tref can be either an `as` conversion, or `try_from` + let name = names.func_param(¶m.name); + let interface_type = names.type_ref(¶m.tref); + quote!( let #name = #name as #interface_type; ) + } + _ => unimplemented!(), + }); + let trait_args = func + .params + .iter() + .map(|param| names.func_param(¶m.name)); + + let trait_rets = func + .results + .iter() + .skip(1) + .map(|result| names.func_param(&result.name)) + .collect::>(); + let (trait_rets, trait_bindings) = if trait_rets.is_empty() { + (quote!({}), quote!(_)) + } else { + let tuple = quote!((#(#trait_rets),*)); + (tuple.clone(), tuple) + }; + + let marshal_rets = func + .results + .iter() + .skip(1) + .map(|_result| quote! { unimplemented!("convert result..."); }); + + quote!(pub fn #ident(#abi_args) -> #abi_ret { + #(#marshal_args)* + let #trait_bindings = match ctx.#ident(#(#trait_args),*) { + Ok(#trait_bindings) => #trait_rets, + Err(e) => { return #err_val; }, + }; + #(#marshal_rets)* + let success:#err_type = ::memory::GuestError::success(); + #abi_ret::from(success) + }) } diff --git a/crates/generate/src/lib.rs b/crates/generate/src/lib.rs index 76e1014c7e..49d9289d58 100644 --- a/crates/generate/src/lib.rs +++ b/crates/generate/src/lib.rs @@ -1,6 +1,7 @@ extern crate proc_macro; mod funcs; +mod module_trait; mod names; mod parse; mod types; @@ -10,6 +11,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use funcs::define_func; +use module_trait::define_module_trait; use names::Names; use types::define_datatype; @@ -27,12 +29,14 @@ pub fn from_witx(args: TokenStream) -> TokenStream { let modules = doc.modules().map(|module| { let modname = names.module(&module.name); let fs = module.funcs().map(|f| define_func(&names, &f)); + let modtrait = define_module_trait(&names, &module); quote!( mod #modname { - use super::*; + use super::WasiCtx; use super::types::*; - use memory::*; #(#fs)* + + #modtrait } ) }); diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs new file mode 100644 index 0000000000..a25addd710 --- /dev/null +++ b/crates/generate/src/module_trait.rs @@ -0,0 +1,33 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::names::Names; +use witx::Module; + +pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { + let traitname = names.trait_name(&m.name); + let traitmethods = m.funcs().map(|f| { + let funcname = names.func(&f.name); + let args = f.params.iter().map(|arg| { + let arg_name = names.func_param(&arg.name); + let arg_type = names.type_ref(&arg.tref); + quote!(#arg_name: #arg_type) + }); + let rets = f + .results + .iter() + .skip(1) + .map(|ret| names.type_ref(&ret.tref)); + let err = f + .results + .get(0) + .map(|err_result| names.type_ref(&err_result.tref)) + .unwrap_or(quote!(())); + quote!(fn #funcname(&mut self, #(#args),*) -> Result<(#(#rets),*), #err>;) + }); + quote! { + pub trait #traitname { + #(#traitmethods)* + } + } +} diff --git a/crates/generate/src/names.rs b/crates/generate/src/names.rs index bd296ac88e..78cec1e0b8 100644 --- a/crates/generate/src/names.rs +++ b/crates/generate/src/names.rs @@ -42,7 +42,6 @@ impl Names { } } - #[allow(unused)] pub fn type_ref(&self, tref: &TypeRef) -> TokenStream { match tref { TypeRef::Name(nt) => { @@ -69,6 +68,10 @@ impl Names { format_ident!("{}", id.as_str().to_snake_case()) } + pub fn trait_name(&self, id: &Id) -> Ident { + format_ident!("{}", id.as_str().to_camel_case()) + } + pub fn func(&self, id: &Id) -> Ident { format_ident!("{}", id.as_str().to_snake_case()) } diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 144b11f2e1..7d34f9688d 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -32,6 +32,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS let ident = names.type_(&name); let repr = int_repr_tokens(e.repr); + let signed_repr = int_signed_repr_tokens(e.repr); let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { @@ -62,6 +63,13 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } + impl ::std::convert::TryFrom<#signed_repr> for #ident { + type Error = ::memory::GuestValueError; + fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { + #ident::try_from(value as #repr) + } + } + impl From<#ident> for #repr { fn from(e: #ident) -> #repr { match e { @@ -70,6 +78,12 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } + impl From<#ident> for #signed_repr { + fn from(e: #ident) -> #signed_repr { + #repr::from(e) as #signed_repr + } + } + impl ::memory::GuestType for #ident { fn size() -> u32 { ::std::mem::size_of::<#repr>() as u32 @@ -109,3 +123,11 @@ fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { witx::IntRepr::U64 => quote!(u64), } } +fn int_signed_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { + match int_repr { + witx::IntRepr::U8 => quote!(i8), + witx::IntRepr::U16 => quote!(i16), + witx::IntRepr::U32 => quote!(i32), + witx::IntRepr::U64 => quote!(i64), + } +} diff --git a/crates/memory/src/guest_type.rs b/crates/memory/src/guest_type.rs index 933b11c33e..9410e6054d 100644 --- a/crates/memory/src/guest_type.rs +++ b/crates/memory/src/guest_type.rs @@ -69,7 +69,7 @@ builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char); pub trait GuestError { type Context; - fn is_success(&self) -> bool; + fn success() -> Self; fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self; fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self; } diff --git a/src/lib.rs b/src/lib.rs index cdb98b3dd4..79e3ebc7c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,17 +7,21 @@ pub mod test { value_errors: Vec<::memory::GuestValueError>, } + impl foo::Foo for WasiCtx { + fn bar(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + println!("BAR: {} {}", an_int, an_float); + Ok(()) + } + } + // Errno is used as a first return value in the functions above, therefore // it must implement GuestError with type Context = WasiCtx. // The context type should let you do logging or debugging or whatever you need // with these errors. We just push them to vecs. impl ::memory::GuestError for types::Errno { type Context = WasiCtx; - fn is_success(&self) -> bool { - match self { - types::Errno::Ok => true, - _ => false, - } + fn success() -> types::Errno { + types::Errno::Ok } fn from_memory_error(e: ::memory::MemoryError, ctx: &mut WasiCtx) -> types::Errno { ctx.mem_errors.push(e);