generate: now we have a way to do errors, i guess
This commit is contained in:
45
crates/generate/src/errors.rs
Normal file
45
crates/generate/src/errors.rs
Normal 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)*
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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(¶m.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(¶m.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!() })
|
||||
}
|
||||
|
||||
@@ -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)*
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
17
src/lib.rs
17
src/lib.rs
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user