some progress on marshalling args

This commit is contained in:
Pat Hickey
2020-01-23 17:55:59 -08:00
parent b4f21752b0
commit b6d342ccb5
4 changed files with 89 additions and 23 deletions

View File

@@ -1,4 +1,4 @@
use proc_macro2::{Ident, TokenStream}; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use crate::names::Names; use crate::names::Names;
@@ -66,29 +66,20 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
let marshal_args = func let marshal_args = func
.params .params
.iter() .iter()
.map(|param| match param.tref.type_().passed_by() { .map(|p| marshal_arg(names, p, func.results.get(0).map(|r| &r.tref)));
witx::TypePassedBy::Value(_atom) => {
// FIXME atom -> param.tref can be either an `as` conversion, or `try_from`
let name = names.func_param(&param.name);
let interface_type = names.type_ref(&param.tref);
quote!( let #name = #name as #interface_type; )
}
_ => unimplemented!(),
});
let trait_args = func let trait_args = func
.params .params
.iter() .iter()
.map(|param| names.func_param(&param.name)); .map(|param| names.func_param(&param.name));
let (trait_rets, trait_bindings) = if func.results.len() < 2 {
(quote!({}), quote!(_))
} else {
let trait_rets = func let trait_rets = func
.results .results
.iter() .iter()
.skip(1) .skip(1)
.map(|result| names.func_param(&result.name)) .map(|result| names.func_param(&result.name));
.collect::<Vec<Ident>>();
let (trait_rets, trait_bindings) = if trait_rets.is_empty() {
(quote!({}), quote!(_))
} else {
let tuple = quote!((#(#trait_rets),*)); let tuple = quote!((#(#trait_rets),*));
(tuple.clone(), tuple) (tuple.clone(), tuple)
}; };
@@ -110,3 +101,67 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
#abi_ret::from(success) #abi_ret::from(success)
}) })
} }
fn marshal_arg(
names: &Names,
param: &witx::InterfaceFuncParam,
error_type: Option<&witx::TypeRef>,
) -> TokenStream {
let tref = &param.tref;
let interface_typename = names.type_ref(&tref);
let name = names.func_param(&param.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"),
}
}

View File

@@ -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 variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name));
let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| {
let variant_name = names.enum_variant(&v.name); let variant_name = names.enum_variant(&v.name);
let n = n as u32;
quote!(#n => Ok(#ident::#variant_name)) quote!(#n => Ok(#ident::#variant_name))
}); });
let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| { let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| {
let variant_name = names.enum_variant(&v.name); let variant_name = names.enum_variant(&v.name);
let n = n as u32; quote!(#ident::#variant_name => #n as #repr)
quote!(#ident::#variant_name => #n)
}); });
quote! { quote! {
@@ -56,14 +54,14 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS
impl ::std::convert::TryFrom<#repr> for #ident { impl ::std::convert::TryFrom<#repr> for #ident {
type Error = ::memory::GuestValueError; type Error = ::memory::GuestValueError;
fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> { fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> {
match value { match value as usize {
#(#tryfrom_repr_cases),*, #(#tryfrom_repr_cases),*,
_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))), _ => 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; type Error = ::memory::GuestValueError;
fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> { fn try_from(value: #signed_repr) -> Result<#ident, ::memory::GuestValueError> {
#ident::try_from(value as #repr) #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 { fn from(e: #ident) -> #signed_repr {
#repr::from(e) as #signed_repr #repr::from(e) as #signed_repr
} }

View File

@@ -12,6 +12,10 @@ pub mod test {
println!("BAR: {} {}", an_int, an_float); println!("BAR: {} {}", an_int, an_float);
Ok(()) 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 // Errno is used as a first return value in the functions above, therefore

View File

@@ -8,9 +8,18 @@
$physically_unable $physically_unable
$picket_line)) $picket_line))
(typename $excuse
(enum u8
$dog_ate
$traffic
$sleeping))
(module $foo (module $foo
(@interface func (export "bar") (@interface func (export "bar")
(param $an_int u32) (param $an_int u32)
(param $an_float f32) (param $an_float f32)
(result $error $errno)) (result $error $errno))
(@interface func (export "baz")
(param $an_excuse $excuse)
(result $error $errno))
) )