generate a module trait and call it
This commit is contained in:
@@ -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::<Vec<Ident>>();
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
33
crates/generate/src/module_trait.rs
Normal file
33
crates/generate/src/module_trait.rs
Normal file
@@ -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)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
14
src/lib.rs
14
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);
|
||||
|
||||
Reference in New Issue
Block a user