generate: now we have a way to do errors, i guess

This commit is contained in:
Pat Hickey
2020-01-22 20:47:40 -08:00
parent 97077954f8
commit c05475b806
8 changed files with 158 additions and 96 deletions

View File

@@ -0,0 +1,45 @@
use crate::names::Names;
use heck::SnakeCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::collections::HashSet;
use witx::{Document, TypeRef};
/// The context struct needs to implement a trait for converting memory and value errors into the
/// witx doc's error types.
pub fn define_error_trait(names: &Names, doc: &Document) -> TokenStream {
// All non-anonymous first return types are used to pass errors.
let error_typenames = doc
.modules()
.flat_map(|m| {
m.funcs()
.filter_map(|f| {
f.results.get(0).and_then(|r| match &r.tref {
TypeRef::Name(nt) => Some(nt.name.clone()),
_ => None,
})
})
.collect::<HashSet<witx::Id>>()
})
.collect::<HashSet<witx::Id>>();
let methods = error_typenames.iter().map(|typename| {
let tname = names.type_(typename);
let methodfragment = typename.as_str().to_snake_case();
let success = format_ident!("success_to_{}", methodfragment);
let memory_error = format_ident!("memory_error_to_{}", methodfragment);
let value_error = format_ident!("value_error_to_{}", methodfragment);
quote! {
fn #success(&mut self) -> #tname;
fn #memory_error(&mut self, err: ::memory::MemoryError) -> #tname;
fn #value_error(&mut self, err: ::memory::GuestValueError) -> #tname;
}
});
quote!(
pub trait WitxErrorConversion {
#(#methods)*
}
)
}

View File

@@ -3,44 +3,45 @@ use quote::quote;
use crate::names::Names;
// FIXME need to template what arguments are required to an import function - some context
// 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.
// Rather than pass in memory as a `&mut [u8]` as today, require a `GuestMemory<'a>` be
// passed in.
//
// 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);
let mut args = quote!(wasi_ctx: &mut WasiCtx, memory: GuestMemory,);
let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream {
let name = names.func_param(&param.name);
match param.tref.type_().passed_by() {
witx::TypePassedBy::Value(atom) => {
let atom = names.atom_type(atom);
quote!(#name: #atom,)
quote!(#name: #atom)
}
witx::TypePassedBy::Pointer => {
let atom = names.atom_type(witx::AtomType::I32);
quote!(#name: #atom,)
quote!(#name: #atom)
}
witx::TypePassedBy::PointerLengthPair => {
let atom = names.atom_type(witx::AtomType::I32);
let len_name = names.func_len_param(&param.name);
quote!(#name: #atom, #len_name, #atom,)
quote!(#name: #atom, #len_name: #atom)
}
}
};
for param in func.params.iter() {
args.extend(arg_signature(param));
}
if let Some(arg_results) = func.results.get(1..) {
for result in arg_results {
args.extend(arg_signature(result))
}
}
let ret = if let Some(first_result) = func.results.get(0) {
let params = func
.params
.iter()
.chain(func.results.iter().skip(1))
.map(arg_signature);
let abi_args = quote!(
wasi_ctx: &mut WasiCtx, memory: GuestMemory,
#(#params),*
);
let abi_ret = if let Some(first_result) = func.results.get(0) {
match first_result.tref.type_().passed_by() {
witx::TypePassedBy::Value(atom) => names.atom_type(atom),
_ => unreachable!("first result should always be passed by value"),
@@ -51,5 +52,5 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
quote!(())
};
quote!(pub fn #ident(#args) -> #ret { unimplemented!() })
quote!(pub fn #ident(#abi_args) -> #abi_ret { unimplemented!() })
}

View File

@@ -1,5 +1,6 @@
extern crate proc_macro;
mod errors;
mod funcs;
mod names;
mod parse;
@@ -9,6 +10,7 @@ use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use errors::define_error_trait;
use funcs::define_func;
use names::Names;
use types::define_datatype;
@@ -22,29 +24,28 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
let doc = witx::load(&witx_paths).expect("loading witx");
let mut types = TokenStream2::new();
for namedtype in doc.typenames() {
let def = define_datatype(&names, &namedtype);
types.extend(def);
}
let types = doc.typenames().map(|t| define_datatype(&names, &t));
let mut modules = TokenStream2::new();
for module in doc.modules() {
let modules = doc.modules().map(|module| {
let modname = names.module(&module.name);
let mut fs = TokenStream2::new();
for func in module.funcs() {
fs.extend(define_func(&names, &func));
}
modules.extend(quote!(
let fs = module.funcs().map(|f| define_func(&names, &f));
quote!(
mod #modname {
use super::*;
use super::types::*;
use memory::*;
#fs
}
));
#(#fs)*
}
)
});
TokenStream::from(quote!(mod types { #types } #modules))
let error_trait = define_error_trait(&names, &doc);
TokenStream::from(quote!(
mod types {
#(#types)*
#error_trait
}
#(#modules)*
))
}

View File

@@ -12,8 +12,9 @@ impl Names {
pub fn new() -> Names {
Names {}
}
pub fn type_(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_camel_case())
pub fn type_(&self, id: &Id) -> TokenStream {
let ident = format_ident!("{}", id.as_str().to_camel_case());
quote!(#ident)
}
pub fn builtin_type(&self, b: BuiltinType) -> TokenStream {
match b {

View File

@@ -31,34 +31,33 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt
fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
let ident = names.type_(&name);
let mut output = TokenStream::new();
let repr = int_repr_tokens(e.repr);
output.extend(quote!(#[repr(#repr)]));
output.extend(quote!(#[derive(Copy, Clone, Debug, std::hash::Hash, Eq, PartialEq)]));
let mut variants = TokenStream::new();
let mut to_repr_cases = TokenStream::new();
let mut tryfrom_repr_cases = TokenStream::new();
for (n, variant) in e.variants.iter().enumerate() {
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;
let variant_name = names.enum_variant(&variant.name);
variants.extend(quote!(#variant_name,));
tryfrom_repr_cases.extend(quote!(#n => Ok(#ident::#variant_name),));
to_repr_cases.extend(quote!(#ident::#variant_name => #n,));
}
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)
});
tryfrom_repr_cases
.extend(quote!(_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident)))));
output.extend(quote!(pub enum #ident {
#variants
quote! {
#[repr(#repr)]
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
pub enum #ident {
#(#variant_names),*
}
impl ::std::convert::TryFrom<#repr> for #ident {
type Error = ::memory::GuestValueError;
fn try_from(value: #repr) -> Result<#ident, ::memory::GuestValueError> {
match value {
#tryfrom_repr_cases
#(#tryfrom_repr_cases),*,
_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident))),
}
}
}
@@ -66,7 +65,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS
impl From<#ident> for #repr {
fn from(e: #ident) -> #repr {
match e {
#to_repr_cases
#(#to_repr_cases),*
}
}
}
@@ -93,9 +92,7 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS
};
}
}
));
output
}
}
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {

View File

@@ -4,5 +4,5 @@ mod memory;
mod region;
pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError};
pub use region::Region;

View File

@@ -1,6 +1,21 @@
pub mod test {
pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here
generate::from_witx!("test.witx");
pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here
impl types::WitxErrorConversion for WasiCtx {
fn success_to_errno(&mut self) -> types::Errno {
types::Errno::Ok
}
fn memory_error_to_errno(&mut self, e: ::memory::MemoryError) -> types::Errno {
eprintln!("memory error: {:?}", e);
types::Errno::InvalidArg
}
fn value_error_to_errno(&mut self, e: ::memory::GuestValueError) -> types::Errno {
eprintln!("guest value error: {:?}", e);
types::Errno::InvalidArg
}
}
}
/*
pub mod wasi {

View File

@@ -2,6 +2,8 @@
(typename $errno
(enum u32
$ok
$invalid_arg
$dont_want_to
$physically_unable
$picket_line))