wiggle: new error configuration for generating a "trappable error" (#5276)

* Add a new "trappable" mode for wiggle to make an error type

start refactoring how errors are generated and configured

put a pin in this - you can now configure a generated error

but i need to go fix Names

Names is no longer a struct, rt is hardcoded to wiggle

rest of fixes to pass tests

its called a trappable error now

don't generate UserErrorConversion trait if empty

mention in macro docs

* undo omitting the user error conversion trait when empty
This commit is contained in:
Pat Hickey
2022-11-16 08:54:41 -08:00
committed by GitHub
parent 1bd78f1a35
commit 22433ed726
15 changed files with 561 additions and 464 deletions

View File

@@ -1,6 +1,6 @@
use crate::config::{AsyncConf, ErrorConf, TracingConf}; use crate::config::{AsyncConf, ErrorConf, ErrorConfField, TracingConf};
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use proc_macro2::TokenStream; use proc_macro2::{Ident, TokenStream};
use quote::quote; use quote::quote;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@@ -39,7 +39,7 @@ impl CodegenSettings {
} }
pub struct ErrorTransform { pub struct ErrorTransform {
m: Vec<UserErrorType>, m: Vec<ErrorType>,
} }
impl ErrorTransform { impl ErrorTransform {
@@ -49,7 +49,13 @@ impl ErrorTransform {
pub fn new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error> { pub fn new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error> {
let mut richtype_identifiers = HashMap::new(); let mut richtype_identifiers = HashMap::new();
let m = conf.iter().map(|(ident, field)| let m = conf.iter().map(|(ident, field)|
if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) { match field {
ErrorConfField::Trappable(field) => if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) {
Ok(ErrorType::Generated(TrappableErrorType { abi_type, rich_type: field.rich_error.clone() }))
} else {
Err(anyhow!("No witx typename \"{}\" found", ident.to_string()))
},
ErrorConfField::User(field) => if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) {
if let Some(ident) = field.rich_error.get_ident() { if let Some(ident) = field.rich_error.get_ident() {
if let Some(prior_def) = richtype_identifiers.insert(ident.clone(), field.err_loc.clone()) if let Some(prior_def) = richtype_identifiers.insert(ident.clone(), field.err_loc.clone())
{ {
@@ -58,11 +64,11 @@ impl ErrorTransform {
ident, prior_def ident, prior_def
)); ));
} }
Ok(UserErrorType { Ok(ErrorType::User(UserErrorType {
abi_type, abi_type,
rich_type: field.rich_error.clone(), rich_type: field.rich_error.clone(),
method_fragment: ident.to_string() method_fragment: ident.to_string()
}) }))
} else { } else {
return Err(anyhow!( return Err(anyhow!(
"rich error type must be identifier for now - TODO add ability to provide a corresponding identifier: {:?}", "rich error type must be identifier for now - TODO add ability to provide a corresponding identifier: {:?}",
@@ -71,23 +77,52 @@ impl ErrorTransform {
} }
} }
else { Err(anyhow!("No witx typename \"{}\" found", ident.to_string())) } else { Err(anyhow!("No witx typename \"{}\" found", ident.to_string())) }
}
).collect::<Result<Vec<_>, Error>>()?; ).collect::<Result<Vec<_>, Error>>()?;
Ok(Self { m }) Ok(Self { m })
} }
pub fn iter(&self) -> impl Iterator<Item = &UserErrorType> { pub fn iter(&self) -> impl Iterator<Item = &ErrorType> {
self.m.iter() self.m.iter()
} }
pub fn for_abi_error(&self, tref: &TypeRef) -> Option<&UserErrorType> { pub fn for_abi_error(&self, tref: &TypeRef) -> Option<&ErrorType> {
match tref { match tref {
TypeRef::Name(nt) => self.for_name(nt), TypeRef::Name(nt) => self.for_name(nt),
TypeRef::Value { .. } => None, TypeRef::Value { .. } => None,
} }
} }
pub fn for_name(&self, nt: &NamedType) -> Option<&UserErrorType> { pub fn for_name(&self, nt: &NamedType) -> Option<&ErrorType> {
self.m.iter().find(|u| u.abi_type.name == nt.name) self.m.iter().find(|e| e.abi_type().name == nt.name)
}
}
pub enum ErrorType {
User(UserErrorType),
Generated(TrappableErrorType),
}
impl ErrorType {
pub fn abi_type(&self) -> &NamedType {
match self {
Self::User(u) => &u.abi_type,
Self::Generated(r) => &r.abi_type,
}
}
}
pub struct TrappableErrorType {
abi_type: Rc<NamedType>,
rich_type: Ident,
}
impl TrappableErrorType {
pub fn abi_type(&self) -> TypeRef {
TypeRef::Name(self.abi_type.clone())
}
pub fn typename(&self) -> TokenStream {
let richtype = &self.rich_type;
quote!(#richtype)
} }
} }

View File

@@ -27,6 +27,7 @@ mod kw {
syn::custom_keyword!(wasmtime); syn::custom_keyword!(wasmtime);
syn::custom_keyword!(tracing); syn::custom_keyword!(tracing);
syn::custom_keyword!(disable_for); syn::custom_keyword!(disable_for);
syn::custom_keyword!(trappable);
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -274,14 +275,14 @@ impl Parse for ErrorConf {
content.parse_terminated(Parse::parse)?; content.parse_terminated(Parse::parse)?;
let mut m = HashMap::new(); let mut m = HashMap::new();
for i in items { for i in items {
match m.insert(i.abi_error.clone(), i.clone()) { match m.insert(i.abi_error().clone(), i.clone()) {
None => {} None => {}
Some(prev_def) => { Some(prev_def) => {
return Err(Error::new( return Err(Error::new(
i.err_loc, *i.err_loc(),
format!( format!(
"duplicate definition of rich error type for {:?}: previously defined at {:?}", "duplicate definition of rich error type for {:?}: previously defined at {:?}",
i.abi_error, prev_def.err_loc, i.abi_error(), prev_def.err_loc(),
), ),
)) ))
} }
@@ -291,20 +292,23 @@ impl Parse for ErrorConf {
} }
} }
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct ErrorConfField { pub enum ErrorConfField {
pub abi_error: Ident, Trappable(TrappableErrorConfField),
pub rich_error: syn::Path, User(UserErrorConfField),
pub err_loc: Span, }
impl ErrorConfField {
pub fn abi_error(&self) -> &Ident {
match self {
Self::Trappable(t) => &t.abi_error,
Self::User(u) => &u.abi_error,
}
}
pub fn err_loc(&self) -> &Span {
match self {
Self::Trappable(t) => &t.err_loc,
Self::User(u) => &u.err_loc,
} }
impl std::fmt::Debug for ErrorConfField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorConfField")
.field("abi_error", &self.abi_error)
.field("rich_error", &"(...)")
.field("err_loc", &self.err_loc)
.finish()
} }
} }
@@ -313,12 +317,48 @@ impl Parse for ErrorConfField {
let err_loc = input.span(); let err_loc = input.span();
let abi_error = input.parse::<Ident>()?; let abi_error = input.parse::<Ident>()?;
let _arrow: Token![=>] = input.parse()?; let _arrow: Token![=>] = input.parse()?;
let rich_error = input.parse::<syn::Path>()?;
Ok(ErrorConfField { let lookahead = input.lookahead1();
if lookahead.peek(kw::trappable) {
let _ = input.parse::<kw::trappable>()?;
let rich_error = input.parse()?;
Ok(ErrorConfField::Trappable(TrappableErrorConfField {
abi_error, abi_error,
rich_error, rich_error,
err_loc, err_loc,
}) }))
} else {
let rich_error = input.parse::<syn::Path>()?;
Ok(ErrorConfField::User(UserErrorConfField {
abi_error,
rich_error,
err_loc,
}))
}
}
}
#[derive(Clone, Debug)]
pub struct TrappableErrorConfField {
pub abi_error: Ident,
pub rich_error: Ident,
pub err_loc: Span,
}
#[derive(Clone)]
pub struct UserErrorConfField {
pub abi_error: Ident,
pub rich_error: syn::Path,
pub err_loc: Span,
}
impl std::fmt::Debug for UserErrorConfField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorConfField")
.field("abi_error", &self.abi_error)
.field("rich_error", &"(...)")
.field("err_loc", &self.err_loc)
.finish()
} }
} }

View File

@@ -1,7 +1,7 @@
use crate::codegen_settings::CodegenSettings; use crate::codegen_settings::{CodegenSettings, ErrorType};
use crate::lifetimes::anon_lifetime; use crate::lifetimes::anon_lifetime;
use crate::module_trait::passed_by_reference; use crate::module_trait::passed_by_reference;
use crate::names::Names; use crate::names;
use crate::types::WiggleType; use crate::types::WiggleType;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::quote; use quote::quote;
@@ -9,54 +9,50 @@ use std::mem;
use witx::Instruction; use witx::Instruction;
pub fn define_func( pub fn define_func(
names: &Names,
module: &witx::Module, module: &witx::Module,
func: &witx::InterfaceFunc, func: &witx::InterfaceFunc,
settings: &CodegenSettings, settings: &CodegenSettings,
) -> TokenStream { ) -> TokenStream {
let (ts, _bounds) = _define_func(names, module, func, settings); let (ts, _bounds) = _define_func(module, func, settings);
ts ts
} }
pub fn func_bounds( pub fn func_bounds(
names: &Names,
module: &witx::Module, module: &witx::Module,
func: &witx::InterfaceFunc, func: &witx::InterfaceFunc,
settings: &CodegenSettings, settings: &CodegenSettings,
) -> Vec<Ident> { ) -> Vec<Ident> {
let (_ts, bounds) = _define_func(names, module, func, settings); let (_ts, bounds) = _define_func(module, func, settings);
bounds bounds
} }
fn _define_func( fn _define_func(
names: &Names,
module: &witx::Module, module: &witx::Module,
func: &witx::InterfaceFunc, func: &witx::InterfaceFunc,
settings: &CodegenSettings, settings: &CodegenSettings,
) -> (TokenStream, Vec<Ident>) { ) -> (TokenStream, Vec<Ident>) {
let rt = names.runtime_mod(); let ident = names::func(&func.name);
let ident = names.func(&func.name);
let (wasm_params, wasm_results) = func.wasm_signature(); let (wasm_params, wasm_results) = func.wasm_signature();
let param_names = (0..wasm_params.len()) let param_names = (0..wasm_params.len())
.map(|i| Ident::new(&format!("arg{}", i), Span::call_site())) .map(|i| Ident::new(&format!("arg{}", i), Span::call_site()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let abi_params = wasm_params.iter().zip(&param_names).map(|(arg, name)| { let abi_params = wasm_params.iter().zip(&param_names).map(|(arg, name)| {
let wasm = names.wasm_type(*arg); let wasm = names::wasm_type(*arg);
quote!(#name : #wasm) quote!(#name : #wasm)
}); });
let abi_ret = match wasm_results.len() { let abi_ret = match wasm_results.len() {
0 => quote!(()), 0 => quote!(()),
1 => { 1 => {
let ty = names.wasm_type(wasm_results[0]); let ty = names::wasm_type(wasm_results[0]);
quote!(#ty) quote!(#ty)
} }
_ => unimplemented!(), _ => unimplemented!(),
}; };
let mut body = TokenStream::new(); let mut body = TokenStream::new();
let mut bounds = vec![names.trait_name(&module.name)]; let mut bounds = vec![names::trait_name(&module.name)];
func.call_interface( func.call_interface(
&module.name, &module.name,
&mut Rust { &mut Rust {
@@ -64,8 +60,6 @@ fn _define_func(
params: &param_names, params: &param_names,
block_storage: Vec::new(), block_storage: Vec::new(),
blocks: Vec::new(), blocks: Vec::new(),
rt: &rt,
names,
module, module,
funcname: func.name.as_str(), funcname: func.name.as_str(),
settings, settings,
@@ -76,8 +70,8 @@ fn _define_func(
let mod_name = &module.name.as_str(); let mod_name = &module.name.as_str();
let func_name = &func.name.as_str(); let func_name = &func.name.as_str();
let mk_span = quote!( let mk_span = quote!(
let _span = #rt::tracing::span!( let _span = wiggle::tracing::span!(
#rt::tracing::Level::TRACE, wiggle::tracing::Level::TRACE,
"wiggle abi", "wiggle abi",
module = #mod_name, module = #mod_name,
function = #func_name function = #func_name
@@ -99,9 +93,9 @@ fn _define_func(
#[allow(unreachable_code)] // deals with warnings in noreturn functions #[allow(unreachable_code)] // deals with warnings in noreturn functions
pub fn #ident( pub fn #ident(
ctx: &mut (impl #(#bounds)+*), ctx: &mut (impl #(#bounds)+*),
memory: &dyn #rt::GuestMemory, memory: &dyn wiggle::GuestMemory,
#(#abi_params),* #(#abi_params),*
) -> #rt::anyhow::Result<#abi_ret> { ) -> wiggle::anyhow::Result<#abi_ret> {
use std::convert::TryFrom as _; use std::convert::TryFrom as _;
#traced_body #traced_body
} }
@@ -111,7 +105,7 @@ fn _define_func(
} else { } else {
let traced_body = if settings.tracing.enabled_for(&mod_name, &func_name) { let traced_body = if settings.tracing.enabled_for(&mod_name, &func_name) {
quote!( quote!(
use #rt::tracing::Instrument as _; use wiggle::tracing::Instrument as _;
#mk_span #mk_span
async move { async move {
#body #body
@@ -129,9 +123,9 @@ fn _define_func(
#[allow(unreachable_code)] // deals with warnings in noreturn functions #[allow(unreachable_code)] // deals with warnings in noreturn functions
pub fn #ident<'a>( pub fn #ident<'a>(
ctx: &'a mut (impl #(#bounds)+*), ctx: &'a mut (impl #(#bounds)+*),
memory: &'a dyn #rt::GuestMemory, memory: &'a dyn wiggle::GuestMemory,
#(#abi_params),* #(#abi_params),*
) -> impl std::future::Future<Output = #rt::anyhow::Result<#abi_ret>> + 'a { ) -> impl std::future::Future<Output = wiggle::anyhow::Result<#abi_ret>> + 'a {
use std::convert::TryFrom as _; use std::convert::TryFrom as _;
#traced_body #traced_body
} }
@@ -146,8 +140,6 @@ struct Rust<'a> {
params: &'a [Ident], params: &'a [Ident],
block_storage: Vec<TokenStream>, block_storage: Vec<TokenStream>,
blocks: Vec<TokenStream>, blocks: Vec<TokenStream>,
rt: &'a TokenStream,
names: &'a Names,
module: &'a witx::Module, module: &'a witx::Module,
funcname: &'a str, funcname: &'a str,
settings: &'a CodegenSettings, settings: &'a CodegenSettings,
@@ -196,17 +188,16 @@ impl witx::Bindgen for Rust<'_> {
operands: &mut Vec<TokenStream>, operands: &mut Vec<TokenStream>,
results: &mut Vec<TokenStream>, results: &mut Vec<TokenStream>,
) { ) {
let rt = self.rt;
let wrap_err = |location: &str| { let wrap_err = |location: &str| {
let modulename = self.module.name.as_str(); let modulename = self.module.name.as_str();
let funcname = self.funcname; let funcname = self.funcname;
quote! { quote! {
|e| { |e| {
#rt::GuestError::InFunc { wiggle::GuestError::InFunc {
modulename: #modulename, modulename: #modulename,
funcname: #funcname, funcname: #funcname,
location: #location, location: #location,
err: Box::new(#rt::GuestError::from(e)), err: Box::new(wiggle::GuestError::from(e)),
} }
} }
} }
@@ -226,9 +217,9 @@ impl witx::Bindgen for Rust<'_> {
Instruction::PointerFromI32 { ty } | Instruction::ConstPointerFromI32 { ty } => { Instruction::PointerFromI32 { ty } | Instruction::ConstPointerFromI32 { ty } => {
let val = operands.pop().unwrap(); let val = operands.pop().unwrap();
let pointee_type = self.names.type_ref(ty, anon_lifetime()); let pointee_type = names::type_ref(ty, anon_lifetime());
results.push(quote! { results.push(quote! {
#rt::GuestPtr::<#pointee_type>::new(memory, #val as u32) wiggle::GuestPtr::<#pointee_type>::new(memory, #val as u32)
}); });
} }
@@ -238,12 +229,12 @@ impl witx::Bindgen for Rust<'_> {
let ty = match &**ty.type_() { let ty = match &**ty.type_() {
witx::Type::Builtin(witx::BuiltinType::Char) => quote!(str), witx::Type::Builtin(witx::BuiltinType::Char) => quote!(str),
_ => { _ => {
let ty = self.names.type_ref(ty, anon_lifetime()); let ty = names::type_ref(ty, anon_lifetime());
quote!([#ty]) quote!([#ty])
} }
}; };
results.push(quote! { results.push(quote! {
#rt::GuestPtr::<#ty>::new(memory, (#ptr as u32, #len as u32)); wiggle::GuestPtr::<#ty>::new(memory, (#ptr as u32, #len as u32));
}) })
} }
@@ -252,7 +243,7 @@ impl witx::Bindgen for Rust<'_> {
// out, and afterwards we call the function with those bindings. // out, and afterwards we call the function with those bindings.
let mut args = Vec::new(); let mut args = Vec::new();
for (i, param) in func.params.iter().enumerate() { for (i, param) in func.params.iter().enumerate() {
let name = self.names.func_param(&param.name); let name = names::func_param(&param.name);
let val = &operands[i]; let val = &operands[i];
self.src.extend(quote!(let #name = #val;)); self.src.extend(quote!(let #name = #val;));
if passed_by_reference(param.tref.type_()) { if passed_by_reference(param.tref.type_()) {
@@ -271,21 +262,21 @@ impl witx::Bindgen for Rust<'_> {
.params .params
.iter() .iter()
.map(|param| { .map(|param| {
let name = self.names.func_param(&param.name); let name = names::func_param(&param.name);
if param.impls_display() { if param.impls_display() {
quote!( #name = #rt::tracing::field::display(&#name) ) quote!( #name = wiggle::tracing::field::display(&#name) )
} else { } else {
quote!( #name = #rt::tracing::field::debug(&#name) ) quote!( #name = wiggle::tracing::field::debug(&#name) )
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.src.extend(quote! { self.src.extend(quote! {
#rt::tracing::event!(#rt::tracing::Level::TRACE, #(#args),*); wiggle::tracing::event!(wiggle::tracing::Level::TRACE, #(#args),*);
}); });
} }
let trait_name = self.names.trait_name(&self.module.name); let trait_name = names::trait_name(&self.module.name);
let ident = self.names.func(&func.name); let ident = names::func(&func.name);
if self.settings.get_async(&self.module, &func).is_sync() { if self.settings.get_async(&self.module, &func).is_sync() {
self.src.extend(quote! { self.src.extend(quote! {
let ret = #trait_name::#ident(ctx, #(#args),*); let ret = #trait_name::#ident(ctx, #(#args),*);
@@ -301,9 +292,9 @@ impl witx::Bindgen for Rust<'_> {
.enabled_for(self.module.name.as_str(), self.funcname) .enabled_for(self.module.name.as_str(), self.funcname)
{ {
self.src.extend(quote! { self.src.extend(quote! {
#rt::tracing::event!( wiggle::tracing::event!(
#rt::tracing::Level::TRACE, wiggle::tracing::Level::TRACE,
result = #rt::tracing::field::debug(&ret), result = wiggle::tracing::field::debug(&ret),
); );
}); });
} }
@@ -322,11 +313,12 @@ impl witx::Bindgen for Rust<'_> {
Instruction::EnumLower { ty } => { Instruction::EnumLower { ty } => {
let val = operands.pop().unwrap(); let val = operands.pop().unwrap();
let val = match self.settings.errors.for_name(ty) { let val = match self.settings.errors.for_name(ty) {
Some(custom) => { Some(ErrorType::User(custom)) => {
let method = self.names.user_error_conversion_method(&custom); let method = names::user_error_conversion_method(&custom);
self.bound(quote::format_ident!("UserErrorConversion")); self.bound(quote::format_ident!("UserErrorConversion"));
quote!(UserErrorConversion::#method(ctx, #val)?) quote!(UserErrorConversion::#method(ctx, #val)?)
} }
Some(ErrorType::Generated(_)) => quote!(#val.downcast()?),
None => val, None => val,
}; };
results.push(quote!(#val as i32)); results.push(quote!(#val as i32));
@@ -336,10 +328,10 @@ impl witx::Bindgen for Rust<'_> {
let err = self.blocks.pop().unwrap(); let err = self.blocks.pop().unwrap();
let ok = self.blocks.pop().unwrap(); let ok = self.blocks.pop().unwrap();
let val = operands.pop().unwrap(); let val = operands.pop().unwrap();
let err_typename = self.names.type_ref(err_ty.unwrap(), anon_lifetime()); let err_typename = names::type_ref(err_ty.unwrap(), anon_lifetime());
results.push(quote! { results.push(quote! {
match #val { match #val {
Ok(e) => { #ok; <#err_typename as #rt::GuestErrorType>::success() as i32 } Ok(e) => { #ok; <#err_typename as wiggle::GuestErrorType>::success() as i32 }
Err(e) => { #err } Err(e) => { #err }
} }
}); });
@@ -369,9 +361,9 @@ impl witx::Bindgen for Rust<'_> {
let ptr = operands.pop().unwrap(); let ptr = operands.pop().unwrap();
let val = operands.pop().unwrap(); let val = operands.pop().unwrap();
let wrap_err = wrap_err(&format!("write {}", ty.name.as_str())); let wrap_err = wrap_err(&format!("write {}", ty.name.as_str()));
let pointee_type = self.names.type_(&ty.name); let pointee_type = names::type_(&ty.name);
self.src.extend(quote! { self.src.extend(quote! {
#rt::GuestPtr::<#pointee_type>::new(memory, #ptr as u32) wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr as u32)
.write(#val) .write(#val)
.map_err(#wrap_err)?; .map_err(#wrap_err)?;
}); });
@@ -380,9 +372,9 @@ impl witx::Bindgen for Rust<'_> {
Instruction::Load { ty } => { Instruction::Load { ty } => {
let ptr = operands.pop().unwrap(); let ptr = operands.pop().unwrap();
let wrap_err = wrap_err(&format!("read {}", ty.name.as_str())); let wrap_err = wrap_err(&format!("read {}", ty.name.as_str()));
let pointee_type = self.names.type_(&ty.name); let pointee_type = names::type_(&ty.name);
results.push(quote! { results.push(quote! {
#rt::GuestPtr::<#pointee_type>::new(memory, #ptr as u32) wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr as u32)
.read() .read()
.map_err(#wrap_err)? .map_err(#wrap_err)?
}); });
@@ -390,7 +382,7 @@ impl witx::Bindgen for Rust<'_> {
Instruction::HandleFromI32 { ty } => { Instruction::HandleFromI32 { ty } => {
let val = operands.pop().unwrap(); let val = operands.pop().unwrap();
let ty = self.names.type_(&ty.name); let ty = names::type_(&ty.name);
results.push(quote!(#ty::from(#val))); results.push(quote!(#ty::from(#val)));
} }
@@ -418,7 +410,7 @@ impl witx::Bindgen for Rust<'_> {
Instruction::EnumLift { ty } Instruction::EnumLift { ty }
| Instruction::BitflagsFromI64 { ty } | Instruction::BitflagsFromI64 { ty }
| Instruction::BitflagsFromI32 { ty } => { | Instruction::BitflagsFromI32 { ty } => {
let ty = self.names.type_(&ty.name); let ty = names::type_(&ty.name);
try_from(quote!(#ty)) try_from(quote!(#ty))
} }

View File

@@ -3,7 +3,7 @@ pub mod config;
mod funcs; mod funcs;
mod lifetimes; mod lifetimes;
mod module_trait; mod module_trait;
mod names; pub mod names;
mod types; mod types;
pub mod wasmtime; pub mod wasmtime;
@@ -12,19 +12,16 @@ use lifetimes::anon_lifetime;
use proc_macro2::{Literal, TokenStream}; use proc_macro2::{Literal, TokenStream};
use quote::quote; use quote::quote;
pub use codegen_settings::{CodegenSettings, UserErrorType}; pub use codegen_settings::{CodegenSettings, ErrorType, UserErrorType};
pub use config::{Config, WasmtimeConfig}; pub use config::{Config, WasmtimeConfig};
pub use funcs::define_func; pub use funcs::define_func;
pub use module_trait::define_module_trait; pub use module_trait::define_module_trait;
pub use names::Names;
pub use types::define_datatype; pub use types::define_datatype;
pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings) -> TokenStream { pub fn generate(doc: &witx::Document, settings: &CodegenSettings) -> TokenStream {
// TODO at some point config should grow more ability to configure name let types = doc
// overrides. .typenames()
let rt = names.runtime_mod(); .map(|t| define_datatype(&t, settings.errors.for_name(&t)));
let types = doc.typenames().map(|t| define_datatype(&names, &t));
let constants = doc.constants().map(|c| { let constants = doc.constants().map(|c| {
let name = quote::format_ident!( let name = quote::format_ident!(
@@ -32,18 +29,24 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings)
c.ty.as_str().to_shouty_snake_case(), c.ty.as_str().to_shouty_snake_case(),
c.name.as_str().to_shouty_snake_case() c.name.as_str().to_shouty_snake_case()
); );
let ty = names.type_(&c.ty); let ty = names::type_(&c.ty);
let value = Literal::u64_unsuffixed(c.value); let value = Literal::u64_unsuffixed(c.value);
quote! { quote! {
pub const #name: #ty = #value; pub const #name: #ty = #value;
} }
}); });
let user_error_methods = settings.errors.iter().map(|errtype| { let user_error_methods = settings.errors.iter().filter_map(|errtype| match errtype {
let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime()); ErrorType::User(errtype) => {
let abi_typename = names::type_ref(&errtype.abi_type(), anon_lifetime());
let user_typename = errtype.typename(); let user_typename = errtype.typename();
let methodname = names.user_error_conversion_method(&errtype); let methodname = names::user_error_conversion_method(&errtype);
quote!(fn #methodname(&mut self, e: super::#user_typename) -> #rt::anyhow::Result<#abi_typename>;) Some(quote! {
fn #methodname(&mut self, e: super::#user_typename)
-> wiggle::anyhow::Result<#abi_typename>;
})
}
ErrorType::Generated(_) => None,
}); });
let user_error_conversion = quote! { let user_error_conversion = quote! {
pub trait UserErrorConversion { pub trait UserErrorConversion {
@@ -51,13 +54,11 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings)
} }
}; };
let modules = doc.modules().map(|module| { let modules = doc.modules().map(|module| {
let modname = names.module(&module.name); let modname = names::module(&module.name);
let fs = module let fs = module.funcs().map(|f| define_func(&module, &f, &settings));
.funcs() let modtrait = define_module_trait(&module, &settings);
.map(|f| define_func(&names, &module, &f, &settings));
let modtrait = define_module_trait(&names, &module, &settings);
let wasmtime = if settings.wasmtime { let wasmtime = if settings.wasmtime {
crate::wasmtime::link_module(&module, &names, None, &settings) crate::wasmtime::link_module(&module, None, &settings)
} else { } else {
quote! {} quote! {}
}; };
@@ -86,14 +87,13 @@ pub fn generate(doc: &witx::Document, names: &Names, settings: &CodegenSettings)
) )
} }
pub fn generate_metadata(doc: &witx::Document, names: &Names) -> TokenStream { pub fn generate_metadata(doc: &witx::Document) -> TokenStream {
let rt = names.runtime_mod();
let doc_text = &format!("{}", doc); let doc_text = &format!("{}", doc);
quote! { quote! {
pub mod metadata { pub mod metadata {
pub const DOC_TEXT: &str = #doc_text; pub const DOC_TEXT: &str = #doc_text;
pub fn document() -> #rt::witx::Document { pub fn document() -> wiggle::witx::Document {
#rt::witx::parse(DOC_TEXT).unwrap() wiggle::witx::parse(DOC_TEXT).unwrap()
} }
} }
} }

View File

@@ -1,9 +1,9 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use crate::codegen_settings::CodegenSettings; use crate::codegen_settings::{CodegenSettings, ErrorType};
use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::lifetimes::{anon_lifetime, LifetimeExt};
use crate::names::Names; use crate::names;
use witx::Module; use witx::Module;
pub fn passed_by_reference(ty: &witx::Type) -> bool { pub fn passed_by_reference(ty: &witx::Type) -> bool {
@@ -15,9 +15,8 @@ pub fn passed_by_reference(ty: &witx::Type) -> bool {
} }
} }
pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings) -> TokenStream { pub fn define_module_trait(m: &Module, settings: &CodegenSettings) -> TokenStream {
let traitname = names.trait_name(&m.name); let traitname = names::trait_name(&m.name);
let rt = names.runtime_mod();
let traitmethods = m.funcs().map(|f| { let traitmethods = m.funcs().map(|f| {
// Check if we're returning an entity anotated with a lifetime, // Check if we're returning an entity anotated with a lifetime,
// in which case, we'll need to annotate the function itself, and // in which case, we'll need to annotate the function itself, and
@@ -32,10 +31,10 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings
} else { } else {
(anon_lifetime(), true) (anon_lifetime(), true)
}; };
let funcname = names.func(&f.name); let funcname = names::func(&f.name);
let args = f.params.iter().map(|arg| { let args = f.params.iter().map(|arg| {
let arg_name = names.func_param(&arg.name); let arg_name = names::func_param(&arg.name);
let arg_typename = names.type_ref(&arg.tref, lifetime.clone()); let arg_typename = names::type_ref(&arg.tref, lifetime.clone());
let arg_type = if passed_by_reference(&*arg.tref.type_()) { let arg_type = if passed_by_reference(&*arg.tref.type_()) {
quote!(&#arg_typename) quote!(&#arg_typename)
} else { } else {
@@ -45,7 +44,7 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings
}); });
let result = match f.results.len() { let result = match f.results.len() {
0 if f.noreturn => quote!(#rt::anyhow::Error), 0 if f.noreturn => quote!(wiggle::anyhow::Error),
0 => quote!(()), 0 => quote!(()),
1 => { 1 => {
let (ok, err) = match &**f.results[0].tref.type_() { let (ok, err) = match &**f.results[0].tref.type_() {
@@ -57,16 +56,17 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings
}; };
let ok = match ok { let ok = match ok {
Some(ty) => names.type_ref(ty, lifetime.clone()), Some(ty) => names::type_ref(ty, lifetime.clone()),
None => quote!(()), None => quote!(()),
}; };
let err = match err { let err = match err {
Some(ty) => match settings.errors.for_abi_error(ty) { Some(ty) => match settings.errors.for_abi_error(ty) {
Some(custom) => { Some(ErrorType::User(custom)) => {
let tn = custom.typename(); let tn = custom.typename();
quote!(super::#tn) quote!(super::#tn)
} }
None => names.type_ref(ty, lifetime.clone()), Some(ErrorType::Generated(g)) => g.typename(),
None => names::type_ref(ty, lifetime.clone()),
}, },
None => quote!(()), None => quote!(()),
}; };
@@ -89,7 +89,7 @@ pub fn define_module_trait(names: &Names, m: &Module, settings: &CodegenSettings
}); });
quote! { quote! {
#[#rt::async_trait] #[wiggle::async_trait]
pub trait #traitname { pub trait #traitname {
#(#traitmethods)* #(#traitmethods)*
} }

View File

@@ -6,25 +6,11 @@ use witx::{BuiltinType, Id, Type, TypeRef, WasmType};
use crate::{lifetimes::LifetimeExt, UserErrorType}; use crate::{lifetimes::LifetimeExt, UserErrorType};
pub struct Names { pub fn type_(id: &Id) -> Ident {
runtime_mod: TokenStream, escape_id(id, NamingConvention::CamelCase)
} }
impl Names { pub fn builtin_type(b: BuiltinType) -> TokenStream {
pub fn new(runtime_mod: TokenStream) -> Names {
Names { runtime_mod }
}
pub fn runtime_mod(&self) -> TokenStream {
self.runtime_mod.clone()
}
pub fn type_(&self, id: &Id) -> TokenStream {
let ident = escape_id(id, NamingConvention::CamelCase);
quote!(#ident)
}
pub fn builtin_type(&self, b: BuiltinType) -> TokenStream {
match b { match b {
BuiltinType::U8 { .. } => quote!(u8), BuiltinType::U8 { .. } => quote!(u8),
BuiltinType::U16 => quote!(u16), BuiltinType::U16 => quote!(u16),
@@ -40,7 +26,7 @@ impl Names {
} }
} }
pub fn wasm_type(&self, ty: WasmType) -> TokenStream { pub fn wasm_type(ty: WasmType) -> TokenStream {
match ty { match ty {
WasmType::I32 => quote!(i32), WasmType::I32 => quote!(i32),
WasmType::I64 => quote!(i64), WasmType::I64 => quote!(i64),
@@ -49,10 +35,10 @@ impl Names {
} }
} }
pub fn type_ref(&self, tref: &TypeRef, lifetime: TokenStream) -> TokenStream { pub fn type_ref(tref: &TypeRef, lifetime: TokenStream) -> TokenStream {
match tref { match tref {
TypeRef::Name(nt) => { TypeRef::Name(nt) => {
let ident = self.type_(&nt.name); let ident = type_(&nt.name);
if nt.tref.needs_lifetime() { if nt.tref.needs_lifetime() {
quote!(#ident<#lifetime>) quote!(#ident<#lifetime>)
} else { } else {
@@ -60,31 +46,28 @@ impl Names {
} }
} }
TypeRef::Value(ty) => match &**ty { TypeRef::Value(ty) => match &**ty {
Type::Builtin(builtin) => self.builtin_type(*builtin), Type::Builtin(builtin) => builtin_type(*builtin),
Type::Pointer(pointee) | Type::ConstPointer(pointee) => { Type::Pointer(pointee) | Type::ConstPointer(pointee) => {
let rt = self.runtime_mod(); let pointee_type = type_ref(&pointee, lifetime.clone());
let pointee_type = self.type_ref(&pointee, lifetime.clone()); quote!(wiggle::GuestPtr<#lifetime, #pointee_type>)
quote!(#rt::GuestPtr<#lifetime, #pointee_type>)
} }
Type::List(pointee) => match &**pointee.type_() { Type::List(pointee) => match &**pointee.type_() {
Type::Builtin(BuiltinType::Char) => { Type::Builtin(BuiltinType::Char) => {
let rt = self.runtime_mod(); quote!(wiggle::GuestPtr<#lifetime, str>)
quote!(#rt::GuestPtr<#lifetime, str>)
} }
_ => { _ => {
let rt = self.runtime_mod(); let pointee_type = type_ref(&pointee, lifetime.clone());
let pointee_type = self.type_ref(&pointee, lifetime.clone()); quote!(wiggle::GuestPtr<#lifetime, [#pointee_type]>)
quote!(#rt::GuestPtr<#lifetime, [#pointee_type]>)
} }
}, },
Type::Variant(v) => match v.as_expected() { Type::Variant(v) => match v.as_expected() {
Some((ok, err)) => { Some((ok, err)) => {
let ok = match ok { let ok = match ok {
Some(ty) => self.type_ref(ty, lifetime.clone()), Some(ty) => type_ref(ty, lifetime.clone()),
None => quote!(()), None => quote!(()),
}; };
let err = match err { let err = match err {
Some(ty) => self.type_ref(ty, lifetime.clone()), Some(ty) => type_ref(ty, lifetime.clone()),
None => quote!(()), None => quote!(()),
}; };
quote!(Result<#ok, #err>) quote!(Result<#ok, #err>)
@@ -95,7 +78,7 @@ impl Names {
let types = r let types = r
.members .members
.iter() .iter()
.map(|m| self.type_ref(&m.tref, lifetime.clone())) .map(|m| type_ref(&m.tref, lifetime.clone()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
quote!((#(#types,)*)) quote!((#(#types,)*))
} }
@@ -108,15 +91,15 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn enum_variant(&self, id: &Id) -> Ident { pub fn enum_variant(id: &Id) -> Ident {
handle_2big_enum_variant(id).unwrap_or_else(|| escape_id(id, NamingConvention::CamelCase)) handle_2big_enum_variant(id).unwrap_or_else(|| escape_id(id, NamingConvention::CamelCase))
} }
pub fn flag_member(&self, id: &Id) -> Ident { pub fn flag_member(id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_shouty_snake_case()) format_ident!("{}", id.as_str().to_shouty_snake_case())
} }
pub fn int_member(&self, id: &Id) -> Ident { pub fn int_member(id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_shouty_snake_case()) format_ident!("{}", id.as_str().to_shouty_snake_case())
} }
@@ -124,7 +107,7 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn struct_member(&self, id: &Id) -> Ident { pub fn struct_member(id: &Id) -> Ident {
escape_id(id, NamingConvention::SnakeCase) escape_id(id, NamingConvention::SnakeCase)
} }
@@ -132,7 +115,7 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn module(&self, id: &Id) -> Ident { pub fn module(id: &Id) -> Ident {
escape_id(id, NamingConvention::SnakeCase) escape_id(id, NamingConvention::SnakeCase)
} }
@@ -140,7 +123,7 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn trait_name(&self, id: &Id) -> Ident { pub fn trait_name(id: &Id) -> Ident {
escape_id(id, NamingConvention::CamelCase) escape_id(id, NamingConvention::CamelCase)
} }
@@ -148,7 +131,7 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn func(&self, id: &Id) -> Ident { pub fn func(id: &Id) -> Ident {
escape_id(id, NamingConvention::SnakeCase) escape_id(id, NamingConvention::SnakeCase)
} }
@@ -156,17 +139,17 @@ impl Names {
/// ///
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html /// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html /// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
pub fn func_param(&self, id: &Id) -> Ident { pub fn func_param(id: &Id) -> Ident {
escape_id(id, NamingConvention::SnakeCase) escape_id(id, NamingConvention::SnakeCase)
} }
/// For when you need a {name}_ptr binding for passing a value by reference: /// For when you need a {name}_ptr binding for passing a value by reference:
pub fn func_ptr_binding(&self, id: &Id) -> Ident { pub fn func_ptr_binding(id: &Id) -> Ident {
format_ident!("{}_ptr", id.as_str().to_snake_case()) format_ident!("{}_ptr", id.as_str().to_snake_case())
} }
/// For when you need a {name}_len binding for passing an array: /// For when you need a {name}_len binding for passing an array:
pub fn func_len_binding(&self, id: &Id) -> Ident { pub fn func_len_binding(id: &Id) -> Ident {
format_ident!("{}_len", id.as_str().to_snake_case()) format_ident!("{}_len", id.as_str().to_snake_case())
} }
@@ -190,21 +173,20 @@ impl Names {
match tref { match tref {
TypeRef::Name(nt) => nt.name.as_str().to_snake_case(), TypeRef::Name(nt) => nt.name.as_str().to_snake_case(),
TypeRef::Value(ty) => match &**ty { TypeRef::Value(ty) => match &**ty {
Type::Builtin(b) => Self::builtin_name(&b).to_owned(), Type::Builtin(b) => builtin_name(&b).to_owned(),
_ => panic!("unexpected anonymous type: {:?}", ty), _ => panic!("unexpected anonymous type: {:?}", ty),
}, },
} }
} }
pub fn user_error_conversion_method(&self, user_type: &UserErrorType) -> Ident { pub fn user_error_conversion_method(user_type: &UserErrorType) -> Ident {
let abi_type = Self::snake_typename(&user_type.abi_type()); let abi_type = snake_typename(&user_type.abi_type());
format_ident!( format_ident!(
"{}_from_{}", "{}_from_{}",
abi_type, abi_type,
user_type.method_fragment().to_snake_case() user_type.method_fragment().to_snake_case()
) )
} }
}
/// Identifier escaping utilities. /// Identifier escaping utilities.
/// ///

View File

@@ -0,0 +1,53 @@
use crate::codegen_settings::TrappableErrorType;
use crate::names;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn define_error(
name: &witx::Id,
_v: &witx::Variant,
e: &TrappableErrorType,
) -> TokenStream {
let abi_error = names::type_(name);
let rich_error = e.typename();
quote! {
#[derive(Debug)]
pub struct #rich_error {
inner: anyhow::Error,
}
impl std::fmt::Display for #rich_error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
impl std::error::Error for #rich_error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.source()
}
}
impl #rich_error {
pub fn trap(inner: anyhow::Error) -> #rich_error {
Self { inner }
}
pub fn downcast(self) -> Result<#abi_error, anyhow::Error> {
self.inner.downcast()
}
pub fn downcast_ref(&self) -> Option<&#abi_error> {
self.inner.downcast_ref()
}
pub fn context(self, s: impl Into<String>) -> Self {
Self { inner: self.inner.context(s.into()) }
}
}
impl From<#abi_error> for #rich_error {
fn from(abi: #abi_error) -> #rich_error {
#rich_error { inner: anyhow::Error::from(abi) }
}
}
}
}

View File

@@ -1,30 +1,28 @@
use crate::names::Names; use crate::names;
use proc_macro2::{Literal, TokenStream}; use proc_macro2::{Literal, TokenStream};
use quote::quote; use quote::quote;
pub(super) fn define_flags( pub(super) fn define_flags(
names: &Names,
name: &witx::Id, name: &witx::Id,
repr: witx::IntRepr, repr: witx::IntRepr,
record: &witx::RecordDatatype, record: &witx::RecordDatatype,
) -> TokenStream { ) -> TokenStream {
let rt = names.runtime_mod(); let ident = names::type_(&name);
let ident = names.type_(&name); let abi_repr = names::wasm_type(repr.into());
let abi_repr = names.wasm_type(repr.into());
let repr = super::int_repr_tokens(repr); let repr = super::int_repr_tokens(repr);
let mut names_ = vec![]; let mut names_ = vec![];
let mut values_ = vec![]; let mut values_ = vec![];
for (i, member) in record.members.iter().enumerate() { for (i, member) in record.members.iter().enumerate() {
let name = names.flag_member(&member.name); let name = names::flag_member(&member.name);
let value_token = Literal::usize_unsuffixed(1 << i); let value_token = Literal::usize_unsuffixed(1 << i);
names_.push(name); names_.push(name);
values_.push(value_token); values_.push(value_token);
} }
quote! { quote! {
#rt::bitflags::bitflags! { wiggle::bitflags::bitflags! {
pub struct #ident: #repr { pub struct #ident: #repr {
#(const #names_ = #values_;)* #(const #names_ = #values_;)*
} }
@@ -43,10 +41,10 @@ pub(super) fn define_flags(
} }
impl TryFrom<#repr> for #ident { impl TryFrom<#repr> for #ident {
type Error = #rt::GuestError; type Error = wiggle::GuestError;
fn try_from(value: #repr) -> Result<Self, #rt::GuestError> { fn try_from(value: #repr) -> Result<Self, wiggle::GuestError> {
if #repr::from(!#ident::all()) & value != 0 { if #repr::from(!#ident::all()) & value != 0 {
Err(#rt::GuestError::InvalidFlagValue(stringify!(#ident))) Err(wiggle::GuestError::InvalidFlagValue(stringify!(#ident)))
} else { } else {
Ok(#ident { bits: value }) Ok(#ident { bits: value })
} }
@@ -54,8 +52,8 @@ pub(super) fn define_flags(
} }
impl TryFrom<#abi_repr> for #ident { impl TryFrom<#abi_repr> for #ident {
type Error = #rt::GuestError; type Error = wiggle::GuestError;
fn try_from(value: #abi_repr) -> Result<Self, #rt::GuestError> { fn try_from(value: #abi_repr) -> Result<Self, wiggle::GuestError> {
#ident::try_from(#repr::try_from(value)?) #ident::try_from(#repr::try_from(value)?)
} }
} }
@@ -66,7 +64,7 @@ pub(super) fn define_flags(
} }
} }
impl<'a> #rt::GuestType<'a> for #ident { impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 { fn guest_size() -> u32 {
#repr::guest_size() #repr::guest_size()
} }
@@ -75,14 +73,14 @@ pub(super) fn define_flags(
#repr::guest_align() #repr::guest_align()
} }
fn read(location: &#rt::GuestPtr<#ident>) -> Result<#ident, #rt::GuestError> { fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> {
use std::convert::TryFrom; use std::convert::TryFrom;
let reprval = #repr::read(&location.cast())?; let reprval = #repr::read(&location.cast())?;
let value = #ident::try_from(reprval)?; let value = #ident::try_from(reprval)?;
Ok(value) Ok(value)
} }
fn write(location: &#rt::GuestPtr<'_, #ident>, val: Self) -> Result<(), #rt::GuestError> { fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> {
let val: #repr = #repr::from(val); let val: #repr = #repr::from(val);
#repr::write(&location.cast(), val) #repr::write(&location.cast(), val)
} }

View File

@@ -1,16 +1,11 @@
use crate::names::Names; use crate::names;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use witx::Layout; use witx::Layout;
pub(super) fn define_handle( pub(super) fn define_handle(name: &witx::Id, h: &witx::HandleDatatype) -> TokenStream {
names: &Names, let ident = names::type_(name);
name: &witx::Id,
h: &witx::HandleDatatype,
) -> TokenStream {
let rt = names.runtime_mod();
let ident = names.type_(name);
let size = h.mem_size_align().size as u32; let size = h.mem_size_align().size as u32;
let align = h.mem_size_align().align as usize; let align = h.mem_size_align().align as usize;
quote! { quote! {
@@ -53,7 +48,7 @@ pub(super) fn define_handle(
} }
} }
impl<'a> #rt::GuestType<'a> for #ident { impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 { fn guest_size() -> u32 {
#size #size
} }
@@ -62,11 +57,11 @@ pub(super) fn define_handle(
#align #align
} }
fn read(location: &#rt::GuestPtr<'a, #ident>) -> Result<#ident, #rt::GuestError> { fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> {
Ok(#ident(u32::read(&location.cast())?)) Ok(#ident(u32::read(&location.cast())?))
} }
fn write(location: &#rt::GuestPtr<'_, Self>, val: Self) -> Result<(), #rt::GuestError> { fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
u32::write(&location.cast(), val.0) u32::write(&location.cast(), val.0)
} }
} }

View File

@@ -1,42 +1,49 @@
// mod r#enum; // mod r#enum;
mod error;
mod flags; mod flags;
mod handle; mod handle;
mod record; mod record;
mod variant; mod variant;
use crate::codegen_settings::ErrorType;
use crate::lifetimes::LifetimeExt; use crate::lifetimes::LifetimeExt;
use crate::names::Names; use crate::names;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream { pub fn define_datatype(namedtype: &witx::NamedType, error: Option<&ErrorType>) -> TokenStream {
match &namedtype.tref { match &namedtype.tref {
witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to), witx::TypeRef::Name(alias_to) => define_alias(&namedtype.name, &alias_to),
witx::TypeRef::Value(v) => match &**v { witx::TypeRef::Value(v) => match &**v {
witx::Type::Record(r) => match r.bitflags_repr() { witx::Type::Record(r) => match r.bitflags_repr() {
Some(repr) => flags::define_flags(names, &namedtype.name, repr, &r), Some(repr) => flags::define_flags(&namedtype.name, repr, &r),
None => record::define_struct(names, &namedtype.name, &r), None => record::define_struct(&namedtype.name, &r),
}, },
witx::Type::Variant(v) => variant::define_variant(names, &namedtype.name, &v), witx::Type::Variant(v) => match error {
witx::Type::Handle(h) => handle::define_handle(names, &namedtype.name, &h), Some(ErrorType::Generated(error)) => {
witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b), let d = variant::define_variant(&namedtype.name, &v, true);
let e = error::define_error(&namedtype.name, &v, error);
quote!( #d #e )
}
_ => variant::define_variant(&namedtype.name, &v, false),
},
witx::Type::Handle(h) => handle::define_handle(&namedtype.name, &h),
witx::Type::Builtin(b) => define_builtin(&namedtype.name, *b),
witx::Type::Pointer(p) => { witx::Type::Pointer(p) => {
let rt = names.runtime_mod(); define_witx_pointer(&namedtype.name, quote!(wiggle::GuestPtr), p)
define_witx_pointer(names, &namedtype.name, quote!(#rt::GuestPtr), p)
} }
witx::Type::ConstPointer(p) => { witx::Type::ConstPointer(p) => {
let rt = names.runtime_mod(); define_witx_pointer(&namedtype.name, quote!(wiggle::GuestPtr), p)
define_witx_pointer(names, &namedtype.name, quote!(#rt::GuestPtr), p)
} }
witx::Type::List(arr) => define_witx_list(names, &namedtype.name, &arr), witx::Type::List(arr) => define_witx_list(&namedtype.name, &arr),
}, },
} }
} }
fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream { fn define_alias(name: &witx::Id, to: &witx::NamedType) -> TokenStream {
let ident = names.type_(name); let ident = names::type_(name);
let rhs = names.type_(&to.name); let rhs = names::type_(&to.name);
if to.tref.needs_lifetime() { if to.tref.needs_lifetime() {
quote!(pub type #ident<'a> = #rhs<'a>;) quote!(pub type #ident<'a> = #rhs<'a>;)
} else { } else {
@@ -44,29 +51,27 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt
} }
} }
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream { fn define_builtin(name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {
let ident = names.type_(name); let ident = names::type_(name);
let built = names.builtin_type(builtin); let built = names::builtin_type(builtin);
quote!(pub type #ident = #built;) quote!(pub type #ident = #built;)
} }
fn define_witx_pointer( fn define_witx_pointer(
names: &Names,
name: &witx::Id, name: &witx::Id,
pointer_type: TokenStream, pointer_type: TokenStream,
pointee: &witx::TypeRef, pointee: &witx::TypeRef,
) -> TokenStream { ) -> TokenStream {
let ident = names.type_(name); let ident = names::type_(name);
let pointee_type = names.type_ref(pointee, quote!('a)); let pointee_type = names::type_ref(pointee, quote!('a));
quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;) quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;)
} }
fn define_witx_list(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { fn define_witx_list(name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream {
let ident = names.type_(name); let ident = names::type_(name);
let rt = names.runtime_mod(); let pointee_type = names::type_ref(arr_raw, quote!('a));
let pointee_type = names.type_ref(arr_raw, quote!('a)); quote!(pub type #ident<'a> = wiggle::GuestPtr<'a, [#pointee_type]>;)
quote!(pub type #ident<'a> = #rt::GuestPtr<'a, [#pointee_type]>;)
} }
pub fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { pub fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {

View File

@@ -1,26 +1,21 @@
use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::lifetimes::{anon_lifetime, LifetimeExt};
use crate::names::Names; use crate::names;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use witx::Layout; use witx::Layout;
pub(super) fn define_struct( pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenStream {
names: &Names, let ident = names::type_(name);
name: &witx::Id,
s: &witx::RecordDatatype,
) -> TokenStream {
let rt = names.runtime_mod();
let ident = names.type_(name);
let size = s.mem_size_align().size as u32; let size = s.mem_size_align().size as u32;
let align = s.mem_size_align().align as usize; let align = s.mem_size_align().align as usize;
let member_names = s.members.iter().map(|m| names.struct_member(&m.name)); let member_names = s.members.iter().map(|m| names::struct_member(&m.name));
let member_decls = s.members.iter().map(|m| { let member_decls = s.members.iter().map(|m| {
let name = names.struct_member(&m.name); let name = names::struct_member(&m.name);
let type_ = match &m.tref { let type_ = match &m.tref {
witx::TypeRef::Name(nt) => { witx::TypeRef::Name(nt) => {
let tt = names.type_(&nt.name); let tt = names::type_(&nt.name);
if m.tref.needs_lifetime() { if m.tref.needs_lifetime() {
quote!(#tt<'a>) quote!(#tt<'a>)
} else { } else {
@@ -28,10 +23,10 @@ pub(super) fn define_struct(
} }
} }
witx::TypeRef::Value(ty) => match &**ty { witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => names.builtin_type(*builtin), witx::Type::Builtin(builtin) => names::builtin_type(*builtin),
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(&pointee, quote!('a)); let pointee_type = names::type_ref(&pointee, quote!('a));
quote!(#rt::GuestPtr<'a, #pointee_type>) quote!(wiggle::GuestPtr<'a, #pointee_type>)
} }
_ => unimplemented!("other anonymous struct members: {:?}", m.tref), _ => unimplemented!("other anonymous struct members: {:?}", m.tref),
}, },
@@ -40,27 +35,27 @@ pub(super) fn define_struct(
}); });
let member_reads = s.member_layout().into_iter().map(|ml| { let member_reads = s.member_layout().into_iter().map(|ml| {
let name = names.struct_member(&ml.member.name); let name = names::struct_member(&ml.member.name);
let offset = ml.offset as u32; let offset = ml.offset as u32;
let location = quote!(location.cast::<u8>().add(#offset)?.cast()); let location = quote!(location.cast::<u8>().add(#offset)?.cast());
match &ml.member.tref { match &ml.member.tref {
witx::TypeRef::Name(nt) => { witx::TypeRef::Name(nt) => {
let type_ = names.type_(&nt.name); let type_ = names::type_(&nt.name);
quote! { quote! {
let #name = <#type_ as #rt::GuestType>::read(&#location)?; let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
} }
} }
witx::TypeRef::Value(ty) => match &**ty { witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => { witx::Type::Builtin(builtin) => {
let type_ = names.builtin_type(*builtin); let type_ = names::builtin_type(*builtin);
quote! { quote! {
let #name = <#type_ as #rt::GuestType>::read(&#location)?; let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
} }
} }
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(&pointee, anon_lifetime()); let pointee_type = names::type_ref(&pointee, anon_lifetime());
quote! { quote! {
let #name = <#rt::GuestPtr::<#pointee_type> as #rt::GuestType>::read(&#location)?; let #name = <wiggle::GuestPtr::<#pointee_type> as wiggle::GuestType>::read(&#location)?;
} }
} }
_ => unimplemented!("other anonymous struct members: {:?}", ty), _ => unimplemented!("other anonymous struct members: {:?}", ty),
@@ -69,10 +64,10 @@ pub(super) fn define_struct(
}); });
let member_writes = s.member_layout().into_iter().map(|ml| { let member_writes = s.member_layout().into_iter().map(|ml| {
let name = names.struct_member(&ml.member.name); let name = names::struct_member(&ml.member.name);
let offset = ml.offset as u32; let offset = ml.offset as u32;
quote! { quote! {
#rt::GuestType::write( wiggle::GuestType::write(
&location.cast::<u8>().add(#offset)?.cast(), &location.cast::<u8>().add(#offset)?.cast(),
val.#name, val.#name,
)?; )?;
@@ -91,7 +86,7 @@ pub(super) fn define_struct(
#(#member_decls),* #(#member_decls),*
} }
impl<'a> #rt::GuestType<'a> for #ident #struct_lifetime { impl<'a> wiggle::GuestType<'a> for #ident #struct_lifetime {
fn guest_size() -> u32 { fn guest_size() -> u32 {
#size #size
} }
@@ -100,12 +95,12 @@ pub(super) fn define_struct(
#align #align
} }
fn read(location: &#rt::GuestPtr<'a, Self>) -> Result<Self, #rt::GuestError> { fn read(location: &wiggle::GuestPtr<'a, Self>) -> Result<Self, wiggle::GuestError> {
#(#member_reads)* #(#member_reads)*
Ok(#ident { #(#member_names),* }) Ok(#ident { #(#member_names),* })
} }
fn write(location: &#rt::GuestPtr<'_, Self>, val: Self) -> Result<(), #rt::GuestError> { fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
#(#member_writes)* #(#member_writes)*
Ok(()) Ok(())
} }

View File

@@ -1,13 +1,16 @@
use crate::lifetimes::LifetimeExt; use crate::lifetimes::LifetimeExt;
use crate::names::Names; use crate::names;
use proc_macro2::{Literal, TokenStream}; use proc_macro2::{Literal, TokenStream};
use quote::quote; use quote::quote;
use witx::Layout; use witx::Layout;
pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant) -> TokenStream { pub(super) fn define_variant(
let rt = names.runtime_mod(); name: &witx::Id,
let ident = names.type_(name); v: &witx::Variant,
derive_std_error: bool,
) -> TokenStream {
let ident = names::type_(name);
let size = v.mem_size_align().size as u32; let size = v.mem_size_align().size as u32;
let align = v.mem_size_align().align as usize; let align = v.mem_size_align().align as usize;
let contents_offset = v.payload_offset() as u32; let contents_offset = v.payload_offset() as u32;
@@ -16,9 +19,9 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
let tag_ty = super::int_repr_tokens(v.tag_repr); let tag_ty = super::int_repr_tokens(v.tag_repr);
let variants = v.cases.iter().map(|c| { let variants = v.cases.iter().map(|c| {
let var_name = names.enum_variant(&c.name); let var_name = names::enum_variant(&c.name);
if let Some(tref) = &c.tref { if let Some(tref) = &c.tref {
let var_type = names.type_ref(&tref, lifetime.clone()); let var_type = names::type_ref(&tref, lifetime.clone());
quote!(#var_name(#var_type)) quote!(#var_name(#var_type))
} else { } else {
quote!(#var_name) quote!(#var_name)
@@ -27,13 +30,13 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
let read_variant = v.cases.iter().enumerate().map(|(i, c)| { let read_variant = v.cases.iter().enumerate().map(|(i, c)| {
let i = Literal::usize_unsuffixed(i); let i = Literal::usize_unsuffixed(i);
let variantname = names.enum_variant(&c.name); let variantname = names::enum_variant(&c.name);
if let Some(tref) = &c.tref { if let Some(tref) = &c.tref {
let varianttype = names.type_ref(tref, lifetime.clone()); let varianttype = names::type_ref(tref, lifetime.clone());
quote! { quote! {
#i => { #i => {
let variant_ptr = location.cast::<u8>().add(#contents_offset)?; let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
let variant_val = <#varianttype as #rt::GuestType>::read(&variant_ptr.cast())?; let variant_val = <#varianttype as wiggle::GuestType>::read(&variant_ptr.cast())?;
Ok(#ident::#variantname(variant_val)) Ok(#ident::#variantname(variant_val))
} }
} }
@@ -43,17 +46,17 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
}); });
let write_variant = v.cases.iter().enumerate().map(|(i, c)| { let write_variant = v.cases.iter().enumerate().map(|(i, c)| {
let variantname = names.enum_variant(&c.name); let variantname = names::enum_variant(&c.name);
let write_tag = quote! { let write_tag = quote! {
location.cast().write(#i as #tag_ty)?; location.cast().write(#i as #tag_ty)?;
}; };
if let Some(tref) = &c.tref { if let Some(tref) = &c.tref {
let varianttype = names.type_ref(tref, lifetime.clone()); let varianttype = names::type_ref(tref, lifetime.clone());
quote! { quote! {
#ident::#variantname(contents) => { #ident::#variantname(contents) => {
#write_tag #write_tag
let variant_ptr = location.cast::<u8>().add(#contents_offset)?; let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
<#varianttype as #rt::GuestType>::write(&variant_ptr.cast(), contents)?; <#varianttype as wiggle::GuestType>::write(&variant_ptr.cast(), contents)?;
} }
} }
} else { } else {
@@ -68,26 +71,26 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
let mut extra_derive = quote!(); let mut extra_derive = quote!();
let enum_try_from = if v.cases.iter().all(|c| c.tref.is_none()) { let enum_try_from = if v.cases.iter().all(|c| c.tref.is_none()) {
let tryfrom_repr_cases = v.cases.iter().enumerate().map(|(i, c)| { let tryfrom_repr_cases = v.cases.iter().enumerate().map(|(i, c)| {
let variant_name = names.enum_variant(&c.name); let variant_name = names::enum_variant(&c.name);
let n = Literal::usize_unsuffixed(i); let n = Literal::usize_unsuffixed(i);
quote!(#n => Ok(#ident::#variant_name)) quote!(#n => Ok(#ident::#variant_name))
}); });
let abi_ty = names.wasm_type(v.tag_repr.into()); let abi_ty = names::wasm_type(v.tag_repr.into());
extra_derive = quote!(, Copy); extra_derive = quote!(, Copy);
quote! { quote! {
impl TryFrom<#tag_ty> for #ident { impl TryFrom<#tag_ty> for #ident {
type Error = #rt::GuestError; type Error = wiggle::GuestError;
fn try_from(value: #tag_ty) -> Result<#ident, #rt::GuestError> { fn try_from(value: #tag_ty) -> Result<#ident, wiggle::GuestError> {
match value { match value {
#(#tryfrom_repr_cases),*, #(#tryfrom_repr_cases),*,
_ => Err( #rt::GuestError::InvalidEnumValue(stringify!(#ident))), _ => Err(wiggle::GuestError::InvalidEnumValue(stringify!(#ident))),
} }
} }
} }
impl TryFrom<#abi_ty> for #ident { impl TryFrom<#abi_ty> for #ident {
type Error = #rt::GuestError; type Error = wiggle::GuestError;
fn try_from(value: #abi_ty) -> Result<#ident, #rt::GuestError> { fn try_from(value: #abi_ty) -> Result<#ident, wiggle::GuestError> {
#ident::try_from(#tag_ty::try_from(value)?) #ident::try_from(#tag_ty::try_from(value)?)
} }
} }
@@ -98,7 +101,7 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
let enum_from = if v.cases.iter().all(|c| c.tref.is_none()) { let enum_from = if v.cases.iter().all(|c| c.tref.is_none()) {
let from_repr_cases = v.cases.iter().enumerate().map(|(i, c)| { let from_repr_cases = v.cases.iter().enumerate().map(|(i, c)| {
let variant_name = names.enum_variant(&c.name); let variant_name = names::enum_variant(&c.name);
let n = Literal::usize_unsuffixed(i); let n = Literal::usize_unsuffixed(i);
quote!(#ident::#variant_name => #n) quote!(#ident::#variant_name => #n)
}); });
@@ -121,16 +124,30 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
(quote!(), quote!(, PartialEq #extra_derive)) (quote!(), quote!(, PartialEq #extra_derive))
}; };
let error_impls = if derive_std_error {
quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for #ident {}
}
} else {
quote!()
};
quote! { quote! {
#[derive(Clone, Debug #extra_derive)] #[derive(Clone, Debug #extra_derive)]
pub enum #ident #enum_lifetime { pub enum #ident #enum_lifetime {
#(#variants),* #(#variants),*
} }
#error_impls
#enum_try_from #enum_try_from
#enum_from #enum_from
impl<'a> #rt::GuestType<'a> for #ident #enum_lifetime { impl<'a> wiggle::GuestType<'a> for #ident #enum_lifetime {
fn guest_size() -> u32 { fn guest_size() -> u32 {
#size #size
} }
@@ -139,19 +156,19 @@ pub(super) fn define_variant(names: &Names, name: &witx::Id, v: &witx::Variant)
#align #align
} }
fn read(location: &#rt::GuestPtr<'a, Self>) fn read(location: &wiggle::GuestPtr<'a, Self>)
-> Result<Self, #rt::GuestError> -> Result<Self, wiggle::GuestError>
{ {
let tag = location.cast::<#tag_ty>().read()?; let tag = location.cast::<#tag_ty>().read()?;
match tag { match tag {
#(#read_variant)* #(#read_variant)*
_ => Err(#rt::GuestError::InvalidEnumValue(stringify!(#ident))), _ => Err(wiggle::GuestError::InvalidEnumValue(stringify!(#ident))),
} }
} }
fn write(location: &#rt::GuestPtr<'_, Self>, val: Self) fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self)
-> Result<(), #rt::GuestError> -> Result<(), wiggle::GuestError>
{ {
match val { match val {
#(#write_variant)* #(#write_variant)*

View File

@@ -1,17 +1,17 @@
use crate::config::Asyncness; use crate::config::Asyncness;
use crate::funcs::func_bounds; use crate::funcs::func_bounds;
use crate::{CodegenSettings, Names}; use crate::names;
use crate::CodegenSettings;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use std::collections::HashSet; use std::collections::HashSet;
pub fn link_module( pub fn link_module(
module: &witx::Module, module: &witx::Module,
names: &Names,
target_path: Option<&syn::Path>, target_path: Option<&syn::Path>,
settings: &CodegenSettings, settings: &CodegenSettings,
) -> TokenStream { ) -> TokenStream {
let module_ident = names.module(&module.name); let module_ident = names::module(&module.name);
let send_bound = if settings.async_.contains_async(module) { let send_bound = if settings.async_.contains_async(module) {
quote! { + Send, T: Send } quote! { + Send, T: Send }
@@ -23,8 +23,8 @@ pub fn link_module(
let mut bounds = HashSet::new(); let mut bounds = HashSet::new();
for f in module.funcs() { for f in module.funcs() {
let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str()); let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str());
bodies.push(generate_func(&module, &f, names, target_path, asyncness)); bodies.push(generate_func(&module, &f, target_path, asyncness));
let bound = func_bounds(names, module, &f, settings); let bound = func_bounds(module, &f, settings);
for b in bound { for b in bound {
bounds.insert(b); bounds.insert(b);
} }
@@ -46,14 +46,12 @@ pub fn link_module(
format_ident!("add_{}_to_linker", module_ident) format_ident!("add_{}_to_linker", module_ident)
}; };
let rt = names.runtime_mod();
quote! { quote! {
/// Adds all instance items to the specified `Linker`. /// Adds all instance items to the specified `Linker`.
pub fn #func_name<T, U>( pub fn #func_name<T, U>(
linker: &mut #rt::wasmtime_crate::Linker<T>, linker: &mut wiggle::wasmtime_crate::Linker<T>,
get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> #rt::anyhow::Result<()> ) -> wiggle::anyhow::Result<()>
where where
U: #ctx_bound #send_bound U: #ctx_bound #send_bound
{ {
@@ -66,17 +64,14 @@ pub fn link_module(
fn generate_func( fn generate_func(
module: &witx::Module, module: &witx::Module,
func: &witx::InterfaceFunc, func: &witx::InterfaceFunc,
names: &Names,
target_path: Option<&syn::Path>, target_path: Option<&syn::Path>,
asyncness: Asyncness, asyncness: Asyncness,
) -> TokenStream { ) -> TokenStream {
let rt = names.runtime_mod();
let module_str = module.name.as_str(); let module_str = module.name.as_str();
let module_ident = names.module(&module.name); let module_ident = names::module(&module.name);
let field_str = func.name.as_str(); let field_str = func.name.as_str();
let field_ident = names.func(&func.name); let field_ident = names::func(&func.name);
let (params, results) = func.wasm_signature(); let (params, results) = func.wasm_signature();
@@ -88,14 +83,14 @@ fn generate_func(
.enumerate() .enumerate()
.map(|(i, ty)| { .map(|(i, ty)| {
let name = &arg_names[i]; let name = &arg_names[i];
let wasm = names.wasm_type(*ty); let wasm = names::wasm_type(*ty);
quote! { #name: #wasm } quote! { #name: #wasm }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let ret_ty = match results.len() { let ret_ty = match results.len() {
0 => quote!(()), 0 => quote!(()),
1 => names.wasm_type(results[0]), 1 => names::wasm_type(results[0]),
_ => unimplemented!(), _ => unimplemented!(),
}; };
@@ -114,16 +109,16 @@ fn generate_func(
let body = quote! { let body = quote! {
let export = caller.get_export("memory"); let export = caller.get_export("memory");
let (mem, ctx) = match &export { let (mem, ctx) = match &export {
Some(#rt::wasmtime_crate::Extern::Memory(m)) => { Some(wiggle::wasmtime_crate::Extern::Memory(m)) => {
let (mem, ctx) = m.data_and_store_mut(&mut caller); let (mem, ctx) = m.data_and_store_mut(&mut caller);
let ctx = get_cx(ctx); let ctx = get_cx(ctx);
(#rt::wasmtime::WasmtimeGuestMemory::new(mem), ctx) (wiggle::wasmtime::WasmtimeGuestMemory::new(mem), ctx)
} }
Some(#rt::wasmtime_crate::Extern::SharedMemory(m)) => { Some(wiggle::wasmtime_crate::Extern::SharedMemory(m)) => {
let ctx = get_cx(caller.data_mut()); let ctx = get_cx(caller.data_mut());
(#rt::wasmtime::WasmtimeGuestMemory::shared(m.data()), ctx) (wiggle::wasmtime::WasmtimeGuestMemory::shared(m.data()), ctx)
} }
_ => #rt::anyhow::bail!("missing required memory export"), _ => wiggle::anyhow::bail!("missing required memory export"),
}; };
Ok(<#ret_ty>::from(#abi_func(ctx, &mem #(, #arg_names)*) #await_ ?)) Ok(<#ret_ty>::from(#abi_func(ctx, &mem #(, #arg_names)*) #await_ ?))
}; };
@@ -135,7 +130,7 @@ fn generate_func(
linker.#wrapper( linker.#wrapper(
#module_str, #module_str,
#field_str, #field_str,
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| { move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| {
Box::new(async move { #body }) Box::new(async move { #body })
}, },
)?; )?;
@@ -147,9 +142,9 @@ fn generate_func(
linker.func_wrap( linker.func_wrap(
#module_str, #module_str,
#field_str, #field_str,
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> #rt::anyhow::Result<#ret_ty> { move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> wiggle::anyhow::Result<#ret_ty> {
let result = async { #body }; let result = async { #body };
#rt::run_in_dummy_executor(result)? wiggle::run_in_dummy_executor(result)?
}, },
)?; )?;
} }
@@ -160,7 +155,7 @@ fn generate_func(
linker.func_wrap( linker.func_wrap(
#module_str, #module_str,
#field_str, #field_str,
move |mut caller: #rt::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> #rt::anyhow::Result<#ret_ty> { move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> wiggle::anyhow::Result<#ret_ty> {
#body #body
}, },
)?; )?;

View File

@@ -39,6 +39,10 @@ use syn::parse_macro_input;
/// `{ errno => YourErrnoType }`. This allows you to use the `UserErrorConversion` /// `{ errno => YourErrnoType }`. This allows you to use the `UserErrorConversion`
/// trait to map these rich errors into the flat witx type, or to terminate /// trait to map these rich errors into the flat witx type, or to terminate
/// WebAssembly execution by trapping. /// WebAssembly execution by trapping.
/// * Instead of requiring the user to define an error type, wiggle can
/// generate an error type for the user which has conversions to/from
/// the base type, and permits trapping, using the syntax
/// `errno => trappable AnErrorType`.
/// * Optional: `async` takes a set of witx modules and functions which are /// * Optional: `async` takes a set of witx modules and functions which are
/// made Rust `async` functions in the module trait. /// made Rust `async` functions in the module trait.
/// ///
@@ -146,7 +150,6 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
let config = parse_macro_input!(args as wiggle_generate::Config); let config = parse_macro_input!(args as wiggle_generate::Config);
let doc = config.load_document(); let doc = config.load_document();
let names = wiggle_generate::Names::new(quote!(wiggle));
let settings = wiggle_generate::CodegenSettings::new( let settings = wiggle_generate::CodegenSettings::new(
&config.errors, &config.errors,
@@ -157,9 +160,9 @@ pub fn from_witx(args: TokenStream) -> TokenStream {
) )
.expect("validating codegen settings"); .expect("validating codegen settings");
let code = wiggle_generate::generate(&doc, &names, &settings); let code = wiggle_generate::generate(&doc, &settings);
let metadata = if cfg!(feature = "wiggle_metadata") { let metadata = if cfg!(feature = "wiggle_metadata") {
wiggle_generate::generate_metadata(&doc, &names) wiggle_generate::generate_metadata(&doc)
} else { } else {
quote!() quote!()
}; };
@@ -188,7 +191,6 @@ pub fn async_trait(attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn wasmtime_integration(args: TokenStream) -> TokenStream { pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig); let config = parse_macro_input!(args as wiggle_generate::WasmtimeConfig);
let doc = config.c.load_document(); let doc = config.c.load_document();
let names = wiggle_generate::Names::new(quote!(wiggle));
let settings = wiggle_generate::CodegenSettings::new( let settings = wiggle_generate::CodegenSettings::new(
&config.c.errors, &config.c.errors,
@@ -200,7 +202,7 @@ pub fn wasmtime_integration(args: TokenStream) -> TokenStream {
.expect("validating codegen settings"); .expect("validating codegen settings");
let modules = doc.modules().map(|module| { let modules = doc.modules().map(|module| {
wiggle_generate::wasmtime::link_module(&module, &names, Some(&config.target), &settings) wiggle_generate::wasmtime::link_module(&module, Some(&config.target), &settings)
}); });
quote!( #(#modules)* ).into() quote!( #(#modules)* ).into()
} }

View File

@@ -24,34 +24,32 @@ mod convert_just_errno {
(param $strike u32) (param $strike u32)
(result $err (expected (error $errno))))) (result $err (expected (error $errno)))))
", ",
errors: { errno => RichError }, errors: { errno => trappable ErrnoT },
}); });
impl_errno!(types::Errno); impl_errno!(types::Errno);
/// When the `errors` mapping in witx is non-empty, we need to impl the impl From<RichError> for types::ErrnoT {
/// types::UserErrorConversion trait that wiggle generates from that mapping. fn from(rich: RichError) -> types::ErrnoT {
impl<'a> types::UserErrorConversion for WasiCtx<'a> { match rich {
fn errno_from_rich_error(&mut self, e: RichError) -> Result<types::Errno> { RichError::InvalidArg(s) => {
// WasiCtx can collect a Vec<String> log so we can test this. We're types::ErrnoT::from(types::Errno::InvalidArg).context(s)
// logging the Display impl that `thiserror::Error` provides us. }
self.log.borrow_mut().push(e.to_string()); RichError::PicketLine(s) => {
// Then do the trivial mapping down to the flat enum. types::ErrnoT::from(types::Errno::PicketLine).context(s)
match e { }
RichError::InvalidArg { .. } => Ok(types::Errno::InvalidArg),
RichError::PicketLine { .. } => Ok(types::Errno::PicketLine),
} }
} }
} }
impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> { impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
fn foo(&mut self, strike: u32) -> Result<(), RichError> { fn foo(&mut self, strike: u32) -> Result<(), types::ErrnoT> {
// We use the argument to this function to exercise all of the // We use the argument to this function to exercise all of the
// possible error cases we could hit here // possible error cases we could hit here
match strike { match strike {
0 => Ok(()), 0 => Ok(()),
1 => Err(RichError::PicketLine(format!("I'm not a scab"))), 1 => Err(RichError::PicketLine(format!("I'm not a scab")))?,
_ => Err(RichError::InvalidArg(format!("out-of-bounds: {}", strike))), _ => Err(RichError::InvalidArg(format!("out-of-bounds: {}", strike)))?,
} }
} }
} }
@@ -78,11 +76,6 @@ mod convert_just_errno {
types::Errno::PicketLine as i32, types::Errno::PicketLine as i32,
"Expected return value for strike=1" "Expected return value for strike=1"
); );
assert_eq!(
ctx.log.borrow_mut().pop().expect("one log entry"),
"Won't cross picket line: I'm not a scab",
"Expected log entry for strike=1",
);
// Second error case: // Second error case:
let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2).unwrap(); let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2).unwrap();
@@ -91,11 +84,6 @@ mod convert_just_errno {
types::Errno::InvalidArg as i32, types::Errno::InvalidArg as i32,
"Expected return value for strike=2" "Expected return value for strike=2"
); );
assert_eq!(
ctx.log.borrow_mut().pop().expect("one log entry"),
"Invalid argument: out-of-bounds: 2",
"Expected log entry for strike=2",
);
} }
} }