From b6d342ccb5691ac0f34b8165d28d3a180f481f4d Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Thu, 23 Jan 2020 17:55:59 -0800 Subject: [PATCH] some progress on marshalling args --- crates/generate/src/funcs.rs | 89 +++++++++++++++++++++++++++++------- crates/generate/src/types.rs | 10 ++-- src/lib.rs | 4 ++ test.witx | 9 ++++ 4 files changed, 89 insertions(+), 23 deletions(-) diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 255e7a90e9..8db2755be7 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; use crate::names::Names; @@ -66,29 +66,20 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { 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!(), - }); + .map(|p| marshal_arg(names, p, func.results.get(0).map(|r| &r.tref))); 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() { + let (trait_rets, trait_bindings) = if func.results.len() < 2 { (quote!({}), quote!(_)) } else { + let trait_rets = func + .results + .iter() + .skip(1) + .map(|result| names.func_param(&result.name)); let tuple = quote!((#(#trait_rets),*)); (tuple.clone(), tuple) }; @@ -110,3 +101,67 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { #abi_ret::from(success) }) } + +fn marshal_arg( + names: &Names, + param: &witx::InterfaceFuncParam, + error_type: Option<&witx::TypeRef>, +) -> TokenStream { + let tref = ¶m.tref; + let interface_typename = names.type_ref(&tref); + let name = names.func_param(¶m.name); + + let value_error_handling = if let Some(tref) = error_type { + let abi_ret = match tref.type_().passed_by() { + witx::TypePassedBy::Value(atom) => names.atom_type(atom), + _ => unreachable!("err should always be passed by value"), + }; + let err_typename = names.type_ref(&tref); + quote! { + let err: #err_typename = ::memory::GuestError::from_memory_error(e, ctx); + return #abi_ret::from(err); + } + } else { + quote! { + panic!("memory error: {:?}", e) + } + }; + + let try_into_conversion = quote! { + use ::std::convert::TryInto; + let #name: #interface_typename = match #name.try_into() { + Ok(a) => a, + Err(e) => { + #value_error_handling + } + }; + }; + + match &*tref.type_() { + witx::Type::Enum(_e) => try_into_conversion, + witx::Type::Builtin(b) => match b { + witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => { + try_into_conversion + } + witx::BuiltinType::S8 | witx::BuiltinType::S16 => quote! { + let #name: #interface_typename = match (#name as i32).try_into() { + Ok(a) => a, + Err(e) => { + #value_error_handling + } + } + }, + witx::BuiltinType::U32 + | witx::BuiltinType::S32 + | witx::BuiltinType::U64 + | witx::BuiltinType::S64 + | witx::BuiltinType::USize + | witx::BuiltinType::F32 + | witx::BuiltinType::F64 => quote! { + let #name = #name as #interface_typename; + }, + witx::BuiltinType::String => unimplemented!("string types unimplemented"), + }, + _ => unimplemented!("only enums and builtins so far"), + } +} diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 7d34f9688d..66df3583fa 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -37,13 +37,11 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS let variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name)); let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let variant_name = names.enum_variant(&v.name); - let n = n as u32; quote!(#n => Ok(#ident::#variant_name)) }); let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let variant_name = names.enum_variant(&v.name); - let n = n as u32; - quote!(#ident::#variant_name => #n) + quote!(#ident::#variant_name => #n as #repr) }); quote! { @@ -56,14 +54,14 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS impl ::std::convert::TryFrom<#repr> for #ident { type Error = ::memory::GuestValueError; fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { - match value { + match value as usize { #(#tryfrom_repr_cases),*, _ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))), } } } - impl ::std::convert::TryFrom<#signed_repr> for #ident { + impl ::std::convert::TryFrom<#signed_repr> for #ident { // XXX this one should always be from i32/i64 (abi size) type Error = ::memory::GuestValueError; fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { #ident::try_from(value as #repr) @@ -78,7 +76,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS } } - impl From<#ident> for #signed_repr { + impl From<#ident> for #signed_repr { // XXX this should be to i32 or i64 (abi size) fn from(e: #ident) -> #signed_repr { #repr::from(e) as #signed_repr } diff --git a/src/lib.rs b/src/lib.rs index 79e3ebc7c7..f6500df8c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,10 @@ pub mod test { println!("BAR: {} {}", an_int, an_float); Ok(()) } + fn baz(&mut self, excuse: types::Excuse) -> Result<(), types::Errno> { + println!("BAZ: {:?}", excuse); + Ok(()) + } } // Errno is used as a first return value in the functions above, therefore diff --git a/test.witx b/test.witx index 940f8594d8..9611b9203b 100644 --- a/test.witx +++ b/test.witx @@ -8,9 +8,18 @@ $physically_unable $picket_line)) +(typename $excuse + (enum u8 + $dog_ate + $traffic + $sleeping)) + (module $foo (@interface func (export "bar") (param $an_int u32) (param $an_float f32) (result $error $errno)) + (@interface func (export "baz") + (param $an_excuse $excuse) + (result $error $errno)) )