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;
|
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.
|
// 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 {
|
pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
|
||||||
let ident = names.func(&func.name);
|
let ident = names.func(&func.name);
|
||||||
let mut args = quote!(wasi_ctx: &mut WasiCtx, memory: GuestMemory,);
|
|
||||||
|
|
||||||
let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream {
|
let arg_signature = |param: &witx::InterfaceFuncParam| -> TokenStream {
|
||||||
let name = names.func_param(¶m.name);
|
let name = names.func_param(¶m.name);
|
||||||
match param.tref.type_().passed_by() {
|
match param.tref.type_().passed_by() {
|
||||||
witx::TypePassedBy::Value(atom) => {
|
witx::TypePassedBy::Value(atom) => {
|
||||||
let atom = names.atom_type(atom);
|
let atom = names.atom_type(atom);
|
||||||
quote!(#name: #atom,)
|
quote!(#name: #atom)
|
||||||
}
|
}
|
||||||
witx::TypePassedBy::Pointer => {
|
witx::TypePassedBy::Pointer => {
|
||||||
let atom = names.atom_type(witx::AtomType::I32);
|
let atom = names.atom_type(witx::AtomType::I32);
|
||||||
quote!(#name: #atom,)
|
quote!(#name: #atom)
|
||||||
}
|
}
|
||||||
witx::TypePassedBy::PointerLengthPair => {
|
witx::TypePassedBy::PointerLengthPair => {
|
||||||
let atom = names.atom_type(witx::AtomType::I32);
|
let atom = names.atom_type(witx::AtomType::I32);
|
||||||
let len_name = names.func_len_param(¶m.name);
|
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() {
|
let params = func
|
||||||
args.extend(arg_signature(param));
|
.params
|
||||||
}
|
.iter()
|
||||||
|
.chain(func.results.iter().skip(1))
|
||||||
if let Some(arg_results) = func.results.get(1..) {
|
.map(arg_signature);
|
||||||
for result in arg_results {
|
let abi_args = quote!(
|
||||||
args.extend(arg_signature(result))
|
wasi_ctx: &mut WasiCtx, memory: GuestMemory,
|
||||||
}
|
#(#params),*
|
||||||
}
|
);
|
||||||
|
let abi_ret = if let Some(first_result) = func.results.get(0) {
|
||||||
let ret = if let Some(first_result) = func.results.get(0) {
|
|
||||||
match first_result.tref.type_().passed_by() {
|
match first_result.tref.type_().passed_by() {
|
||||||
witx::TypePassedBy::Value(atom) => names.atom_type(atom),
|
witx::TypePassedBy::Value(atom) => names.atom_type(atom),
|
||||||
_ => unreachable!("first result should always be passed by value"),
|
_ => 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!(())
|
||||||
};
|
};
|
||||||
|
|
||||||
quote!(pub fn #ident(#args) -> #ret { unimplemented!() })
|
quote!(pub fn #ident(#abi_args) -> #abi_ret { unimplemented!() })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod errors;
|
||||||
mod funcs;
|
mod funcs;
|
||||||
mod names;
|
mod names;
|
||||||
mod parse;
|
mod parse;
|
||||||
@@ -9,6 +10,7 @@ use proc_macro::TokenStream;
|
|||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
|
use errors::define_error_trait;
|
||||||
use funcs::define_func;
|
use funcs::define_func;
|
||||||
use names::Names;
|
use names::Names;
|
||||||
use types::define_datatype;
|
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 doc = witx::load(&witx_paths).expect("loading witx");
|
||||||
|
|
||||||
let mut types = TokenStream2::new();
|
let types = doc.typenames().map(|t| define_datatype(&names, &t));
|
||||||
for namedtype in doc.typenames() {
|
|
||||||
let def = define_datatype(&names, &namedtype);
|
|
||||||
types.extend(def);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut modules = TokenStream2::new();
|
let modules = doc.modules().map(|module| {
|
||||||
for module in doc.modules() {
|
|
||||||
let modname = names.module(&module.name);
|
let modname = names.module(&module.name);
|
||||||
|
let fs = module.funcs().map(|f| define_func(&names, &f));
|
||||||
let mut fs = TokenStream2::new();
|
quote!(
|
||||||
for func in module.funcs() {
|
|
||||||
fs.extend(define_func(&names, &func));
|
|
||||||
}
|
|
||||||
modules.extend(quote!(
|
|
||||||
mod #modname {
|
mod #modname {
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
use memory::*;
|
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 {
|
pub fn new() -> Names {
|
||||||
Names {}
|
Names {}
|
||||||
}
|
}
|
||||||
pub fn type_(&self, id: &Id) -> Ident {
|
pub fn type_(&self, id: &Id) -> TokenStream {
|
||||||
format_ident!("{}", id.as_str().to_camel_case())
|
let ident = format_ident!("{}", id.as_str().to_camel_case());
|
||||||
|
quote!(#ident)
|
||||||
}
|
}
|
||||||
pub fn builtin_type(&self, b: BuiltinType) -> TokenStream {
|
pub fn builtin_type(&self, b: BuiltinType) -> TokenStream {
|
||||||
match b {
|
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 {
|
fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
|
||||||
let ident = names.type_(&name);
|
let ident = names.type_(&name);
|
||||||
|
|
||||||
let mut output = TokenStream::new();
|
|
||||||
let repr = int_repr_tokens(e.repr);
|
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 variant_names = e.variants.iter().map(|v| names.enum_variant(&v.name));
|
||||||
let mut to_repr_cases = TokenStream::new();
|
let tryfrom_repr_cases = e.variants.iter().enumerate().map(|(n, v)| {
|
||||||
let mut tryfrom_repr_cases = TokenStream::new();
|
let variant_name = names.enum_variant(&v.name);
|
||||||
for (n, variant) in e.variants.iter().enumerate() {
|
|
||||||
let n = n as u32;
|
let n = n as u32;
|
||||||
let variant_name = names.enum_variant(&variant.name);
|
quote!(#n => Ok(#ident::#variant_name))
|
||||||
variants.extend(quote!(#variant_name,));
|
});
|
||||||
tryfrom_repr_cases.extend(quote!(#n => Ok(#ident::#variant_name),));
|
let to_repr_cases = e.variants.iter().enumerate().map(|(n, v)| {
|
||||||
to_repr_cases.extend(quote!(#ident::#variant_name => #n,));
|
let variant_name = names.enum_variant(&v.name);
|
||||||
}
|
let n = n as u32;
|
||||||
|
quote!(#ident::#variant_name => #n)
|
||||||
|
});
|
||||||
|
|
||||||
tryfrom_repr_cases
|
quote! {
|
||||||
.extend(quote!(_ => Err(::memory::GuestValueError::InvalidEnum(stringify!(#ident)))));
|
#[repr(#repr)]
|
||||||
|
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||||
output.extend(quote!(pub enum #ident {
|
pub enum #ident {
|
||||||
#variants
|
#(#variant_names),*
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
#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 {
|
impl From<#ident> for #repr {
|
||||||
fn from(e: #ident) -> #repr {
|
fn from(e: #ident) -> #repr {
|
||||||
match e {
|
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 {
|
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ mod memory;
|
|||||||
mod region;
|
mod region;
|
||||||
|
|
||||||
pub use guest_type::{GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError};
|
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;
|
pub use region::Region;
|
||||||
|
|||||||
17
src/lib.rs
17
src/lib.rs
@@ -1,6 +1,21 @@
|
|||||||
pub mod test {
|
pub mod test {
|
||||||
pub struct WasiCtx {} // FIXME: parameterize macro on what ctx type is used here
|
|
||||||
generate::from_witx!("test.witx");
|
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 {
|
pub mod wasi {
|
||||||
|
|||||||
Reference in New Issue
Block a user