Shuffle around the wiggle crates (#1414)

* Shuffle around the wiggle crates

This commit reorganizes the wiggle crates slightly by performing the
following transforms:

* The `crates/wiggle` crate, previously named `wiggle`, was moved to
  `crates/wiggle/crates/macro` and is renamed to `wiggle-macro`.

* The `crates/wiggle/crates/runtime` crate, previously named
  `wiggle-runtime`, was moved to `crates/wiggle` and is renamed to
  `wiggle`.

* The new `wiggle` crate depends on `wiggle-macro` and reexports the macro.

The goal here is that consumers only deal with the `wiggle` crate
itself. No more crates depend on `wiggle-runtime` and all dependencies
are entirely on just the `wiggle` crate.

* Remove the `crates/wiggle/crates` directory

Move everything into `crates/wiggle` directly, like `wasi-common`

* Add wiggle-macro to test-all script

* Fixup a test
This commit is contained in:
Alex Crichton
2020-03-26 18:34:50 -05:00
committed by GitHub
parent 6fa9be7767
commit a628dc315e
47 changed files with 802 additions and 800 deletions

View File

@@ -0,0 +1,117 @@
use std::path::{Path, PathBuf};
use proc_macro2::Span;
use syn::{
braced, bracketed,
parse::{Parse, ParseStream},
punctuated::Punctuated,
Error, Ident, LitStr, Result, Token,
};
#[derive(Debug, Clone)]
pub struct Config {
pub witx: WitxConf,
pub ctx: CtxConf,
pub emit_metadata: bool,
}
#[derive(Debug, Clone)]
pub enum ConfigField {
Witx(WitxConf),
Ctx(CtxConf),
}
impl ConfigField {
pub fn parse_pair(ident: &str, value: ParseStream, err_loc: Span) -> Result<Self> {
match ident {
"witx" => Ok(ConfigField::Witx(value.parse()?)),
"ctx" => Ok(ConfigField::Ctx(value.parse()?)),
_ => Err(Error::new(err_loc, "expected `witx` or `ctx`")),
}
}
}
impl Parse for ConfigField {
fn parse(input: ParseStream) -> Result<Self> {
let id: Ident = input.parse()?;
let _colon: Token![:] = input.parse()?;
Self::parse_pair(id.to_string().as_ref(), input, id.span())
}
}
impl Config {
pub fn build(fields: impl Iterator<Item = ConfigField>, err_loc: Span) -> Result<Self> {
let mut witx = None;
let mut ctx = None;
for f in fields {
match f {
ConfigField::Witx(c) => {
witx = Some(c);
}
ConfigField::Ctx(c) => {
ctx = Some(c);
}
}
}
Ok(Config {
witx: witx
.take()
.ok_or_else(|| Error::new(err_loc, "`witx` field required"))?,
ctx: ctx
.take()
.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
emit_metadata: false,
})
}
}
impl Parse for Config {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<ConfigField, Token![,]> =
contents.parse_terminated(ConfigField::parse)?;
Ok(Config::build(fields.into_iter(), input.span())?)
}
}
#[derive(Debug, Clone)]
pub struct WitxConf {
pub paths: Vec<PathBuf>,
}
impl WitxConf {
pub fn make_paths_relative_to<P: AsRef<Path>>(&mut self, root: P) {
self.paths.iter_mut().for_each(|p| {
if !p.is_absolute() {
*p = PathBuf::from(root.as_ref()).join(p.clone());
}
});
}
}
impl Parse for WitxConf {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let _ = bracketed!(content in input);
let path_lits: Punctuated<LitStr, Token![,]> = content.parse_terminated(Parse::parse)?;
let paths = path_lits
.iter()
.map(|lit| PathBuf::from(lit.value()))
.collect();
Ok(WitxConf { paths })
}
}
#[derive(Debug, Clone)]
pub struct CtxConf {
pub name: Ident,
}
impl Parse for CtxConf {
fn parse(input: ParseStream) -> Result<Self> {
Ok(CtxConf {
name: input.parse()?,
})
}
}

View File

@@ -0,0 +1,264 @@
use proc_macro2::TokenStream;
use quote::quote;
use crate::lifetimes::anon_lifetime;
use crate::module_trait::passed_by_reference;
use crate::names::Names;
pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
let funcname = func.name.as_str();
let ident = names.func(&func.name);
let ctx_type = names.ctx_type();
let coretype = func.core_type();
let params = coretype.args.iter().map(|arg| {
let name = names.func_core_arg(arg);
let atom = names.atom_type(arg.repr());
quote!(#name : #atom)
});
let abi_args = quote!(
ctx: &#ctx_type, memory: &dyn wiggle::GuestMemory,
#(#params),*
);
let abi_ret = if let Some(ret) = &coretype.ret {
match ret.signifies {
witx::CoreParamSignifies::Value(atom) => names.atom_type(atom),
_ => unreachable!("ret should always be passed by value"),
}
} else if func.noreturn {
// Ideally we would return `quote!(!)` here, but, we'd have to change
// the error handling logic in all the marshalling code to never return,
// and instead provide some other way to bail to the context...
// noreturn func
unimplemented!("noreturn funcs not supported yet!")
} else {
quote!(())
};
let err_type = coretype.ret.map(|ret| ret.param.tref);
let err_val = err_type
.clone()
.map(|_res| quote!(#abi_ret::from(e)))
.unwrap_or_else(|| quote!(()));
let error_handling = |location: &str| -> TokenStream {
if let Some(tref) = &err_type {
let abi_ret = match tref.type_().passed_by() {
witx::TypePassedBy::Value(atom) => names.atom_type(atom),
_ => unreachable!("err should always be passed by value"),
};
let err_typename = names.type_ref(&tref, anon_lifetime());
quote! {
let e = wiggle::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e.into()) };
let err: #err_typename = wiggle::GuestErrorType::from_error(e, ctx);
return #abi_ret::from(err);
}
} else {
quote! {
panic!("error: {:?}", e)
}
}
};
let marshal_args = func
.params
.iter()
.map(|p| marshal_arg(names, p, error_handling(p.name.as_str())));
let trait_args = func.params.iter().map(|param| {
let name = names.func_param(&param.name);
if passed_by_reference(&*param.tref.type_()) {
quote!(&#name)
} else {
quote!(#name)
}
});
let (trait_rets, trait_bindings) = if func.results.len() < 2 {
(quote!({}), quote!(_))
} else {
let trait_rets = func
.results
.iter()
.skip(1)
.map(|result| names.func_param(&result.name));
let tuple = quote!((#(#trait_rets),*));
(tuple.clone(), tuple)
};
// Return value pointers need to be validated before the api call, then
// assigned to afterwards. marshal_result returns these two statements as a pair.
let marshal_rets = func
.results
.iter()
.skip(1)
.map(|result| marshal_result(names, result, &error_handling));
let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre);
let marshal_rets_post = marshal_rets.map(|(_pre, post)| post);
let success = if let Some(ref err_type) = err_type {
let err_typename = names.type_ref(&err_type, anon_lifetime());
quote! {
let success:#err_typename = wiggle::GuestErrorType::success();
#abi_ret::from(success)
}
} else {
quote!()
};
quote!(pub fn #ident(#abi_args) -> #abi_ret {
#(#marshal_args)*
#(#marshal_rets_pre)*
let #trait_bindings = match ctx.#ident(#(#trait_args),*) {
Ok(#trait_bindings) => #trait_rets,
Err(e) => { return #err_val; },
};
#(#marshal_rets_post)*
#success
})
}
fn marshal_arg(
names: &Names,
param: &witx::InterfaceFuncParam,
error_handling: TokenStream,
) -> TokenStream {
let tref = &param.tref;
let interface_typename = names.type_ref(&tref, anon_lifetime());
let try_into_conversion = {
let name = names.func_param(&param.name);
quote! {
let #name: #interface_typename = {
use ::std::convert::TryInto;
match #name.try_into() {
Ok(a) => a,
Err(e) => {
#error_handling
}
}
};
}
};
let read_conversion = {
let pointee_type = names.type_ref(tref, anon_lifetime());
let arg_name = names.func_ptr_binding(&param.name);
let name = names.func_param(&param.name);
quote! {
let #name = match wiggle::GuestPtr::<#pointee_type>::new(memory, #arg_name as u32).read() {
Ok(r) => r,
Err(e) => {
#error_handling
}
};
}
};
match &*tref.type_() {
witx::Type::Enum(_e) => try_into_conversion,
witx::Type::Flags(_f) => try_into_conversion,
witx::Type::Int(_i) => try_into_conversion,
witx::Type::Builtin(b) => match b {
witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => {
try_into_conversion
}
witx::BuiltinType::S8 | witx::BuiltinType::S16 => {
let name = names.func_param(&param.name);
quote! {
let #name: #interface_typename = match (#name as i32).try_into() {
Ok(a) => a,
Err(e) => {
#error_handling
}
}
}
}
witx::BuiltinType::U32
| witx::BuiltinType::S32
| witx::BuiltinType::U64
| witx::BuiltinType::S64
| witx::BuiltinType::USize
| witx::BuiltinType::F32
| witx::BuiltinType::F64 => {
let name = names.func_param(&param.name);
quote! {
let #name = #name as #interface_typename;
}
}
witx::BuiltinType::String => {
let lifetime = anon_lifetime();
let ptr_name = names.func_ptr_binding(&param.name);
let len_name = names.func_len_binding(&param.name);
let name = names.func_param(&param.name);
quote! {
let #name = wiggle::GuestPtr::<#lifetime, str>::new(memory, (#ptr_name as u32, #len_name as u32));
}
}
},
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(pointee, anon_lifetime());
let name = names.func_param(&param.name);
quote! {
let #name = wiggle::GuestPtr::<#pointee_type>::new(memory, #name as u32);
}
}
witx::Type::Struct(_) => read_conversion,
witx::Type::Array(arr) => {
let pointee_type = names.type_ref(arr, anon_lifetime());
let ptr_name = names.func_ptr_binding(&param.name);
let len_name = names.func_len_binding(&param.name);
let name = names.func_param(&param.name);
quote! {
let #name = wiggle::GuestPtr::<[#pointee_type]>::new(memory, (#ptr_name as u32, #len_name as u32));
}
}
witx::Type::Union(_u) => read_conversion,
witx::Type::Handle(_h) => {
let name = names.func_param(&param.name);
let handle_type = names.type_ref(tref, anon_lifetime());
quote!( let #name = #handle_type::from(#name); )
}
}
}
fn marshal_result<F>(
names: &Names,
result: &witx::InterfaceFuncParam,
error_handling: F,
) -> (TokenStream, TokenStream)
where
F: Fn(&str) -> TokenStream,
{
let tref = &result.tref;
let write_val_to_ptr = {
let pointee_type = names.type_ref(tref, anon_lifetime());
// core type is given func_ptr_binding name.
let ptr_name = names.func_ptr_binding(&result.name);
let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str()));
let pre = quote! {
let #ptr_name = wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr_name as u32);
};
// trait binding returns func_param name.
let val_name = names.func_param(&result.name);
let post = quote! {
if let Err(e) = #ptr_name.write(#val_name) {
#ptr_err_handling
}
};
(pre, post)
};
match &*tref.type_() {
witx::Type::Builtin(b) => match b {
witx::BuiltinType::String => unimplemented!("string result types"),
_ => write_val_to_ptr,
},
witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } | witx::Type::Array { .. } => {
unimplemented!("pointer/array result types")
}
_ => write_val_to_ptr,
}
}

View File

@@ -0,0 +1,59 @@
pub mod config;
mod funcs;
mod lifetimes;
mod module_trait;
mod names;
mod types;
use proc_macro2::TokenStream;
use quote::quote;
pub use config::Config;
pub use funcs::define_func;
pub use module_trait::define_module_trait;
pub use names::Names;
pub use types::define_datatype;
pub fn generate(doc: &witx::Document, config: &Config) -> TokenStream {
let names = Names::new(config); // TODO parse the names from the invocation of the macro, or from a file?
let types = doc.typenames().map(|t| define_datatype(&names, &t));
let modules = doc.modules().map(|module| {
let modname = names.module(&module.name);
let fs = module.funcs().map(|f| define_func(&names, &f));
let modtrait = define_module_trait(&names, &module);
let ctx_type = names.ctx_type();
quote!(
pub mod #modname {
use super::#ctx_type;
use super::types::*;
#(#fs)*
#modtrait
}
)
});
let metadata = if config.emit_metadata {
let doc_text = &format!("{}", doc);
quote! {
pub mod metadata {
pub const DOC_TEXT: &str = #doc_text;
pub fn document() -> wiggle::witx::Document {
wiggle::witx::parse(DOC_TEXT).unwrap()
}
}
}
} else {
quote!()
};
quote!(
pub mod types {
#(#types)*
}
#(#modules)*
#metadata
)
}

View File

@@ -0,0 +1,83 @@
use proc_macro2::TokenStream;
use quote::quote;
pub trait LifetimeExt {
fn is_transparent(&self) -> bool;
fn needs_lifetime(&self) -> bool;
}
impl LifetimeExt for witx::TypeRef {
fn is_transparent(&self) -> bool {
self.type_().is_transparent()
}
fn needs_lifetime(&self) -> bool {
self.type_().needs_lifetime()
}
}
impl LifetimeExt for witx::Type {
fn is_transparent(&self) -> bool {
match self {
witx::Type::Builtin(b) => b.is_transparent(),
witx::Type::Struct(s) => s.is_transparent(),
witx::Type::Enum { .. }
| witx::Type::Flags { .. }
| witx::Type::Int { .. }
| witx::Type::Handle { .. } => true,
witx::Type::Union { .. }
| witx::Type::Pointer { .. }
| witx::Type::ConstPointer { .. }
| witx::Type::Array { .. } => false,
}
}
fn needs_lifetime(&self) -> bool {
match self {
witx::Type::Builtin(b) => b.needs_lifetime(),
witx::Type::Struct(s) => s.needs_lifetime(),
witx::Type::Union(u) => u.needs_lifetime(),
witx::Type::Enum { .. }
| witx::Type::Flags { .. }
| witx::Type::Int { .. }
| witx::Type::Handle { .. } => false,
witx::Type::Pointer { .. }
| witx::Type::ConstPointer { .. }
| witx::Type::Array { .. } => true,
}
}
}
impl LifetimeExt for witx::BuiltinType {
fn is_transparent(&self) -> bool {
!self.needs_lifetime()
}
fn needs_lifetime(&self) -> bool {
match self {
witx::BuiltinType::String => true,
_ => false,
}
}
}
impl LifetimeExt for witx::StructDatatype {
fn is_transparent(&self) -> bool {
self.members.iter().all(|m| m.tref.is_transparent())
}
fn needs_lifetime(&self) -> bool {
self.members.iter().any(|m| m.tref.needs_lifetime())
}
}
impl LifetimeExt for witx::UnionDatatype {
fn is_transparent(&self) -> bool {
false
}
fn needs_lifetime(&self) -> bool {
self.variants
.iter()
.any(|m| m.tref.as_ref().map(|t| t.needs_lifetime()).unwrap_or(false))
}
}
pub fn anon_lifetime() -> TokenStream {
quote!('_)
}

View File

@@ -0,0 +1,72 @@
use proc_macro2::TokenStream;
use quote::quote;
use crate::lifetimes::{anon_lifetime, LifetimeExt};
use crate::names::Names;
use witx::Module;
pub fn passed_by_reference(ty: &witx::Type) -> bool {
let passed_by = match ty.passed_by() {
witx::TypePassedBy::Value { .. } => false,
witx::TypePassedBy::Pointer { .. } | witx::TypePassedBy::PointerLengthPair { .. } => true,
};
match ty {
witx::Type::Builtin(b) => match &*b {
witx::BuiltinType::String => true,
_ => passed_by,
},
witx::Type::Pointer(_) | witx::Type::ConstPointer(_) | witx::Type::Array(_) => true,
_ => passed_by,
}
}
pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream {
let traitname = names.trait_name(&m.name);
let traitmethods = m.funcs().map(|f| {
// Check if we're returning an entity anotated with a lifetime,
// in which case, we'll need to annotate the function itself, and
// hence will need an explicit lifetime (rather than anonymous)
let (lifetime, is_anonymous) = if f
.params
.iter()
.chain(&f.results)
.any(|ret| ret.tref.needs_lifetime())
{
(quote!('a), false)
} else {
(anon_lifetime(), true)
};
let funcname = names.func(&f.name);
let args = f.params.iter().map(|arg| {
let arg_name = names.func_param(&arg.name);
let arg_typename = names.type_ref(&arg.tref, lifetime.clone());
let arg_type = if passed_by_reference(&*arg.tref.type_()) {
quote!(&#arg_typename)
} else {
quote!(#arg_typename)
};
quote!(#arg_name: #arg_type)
});
let rets = f
.results
.iter()
.skip(1)
.map(|ret| names.type_ref(&ret.tref, lifetime.clone()));
let err = f
.results
.get(0)
.map(|err_result| names.type_ref(&err_result.tref, lifetime.clone()))
.unwrap_or(quote!(()));
if is_anonymous {
quote!(fn #funcname(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
} else {
quote!(fn #funcname<#lifetime>(&self, #(#args),*) -> Result<(#(#rets),*), #err>;)
}
});
quote! {
pub trait #traitname {
#(#traitmethods)*
}
}
}

View File

@@ -0,0 +1,144 @@
use heck::{CamelCase, ShoutySnakeCase, SnakeCase};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use witx::{AtomType, BuiltinType, Id, TypeRef};
use crate::lifetimes::LifetimeExt;
use crate::Config;
#[derive(Debug, Clone)]
pub struct Names {
config: Config,
}
impl Names {
pub fn new(config: &Config) -> Names {
Names {
config: config.clone(),
}
}
pub fn ctx_type(&self) -> Ident {
self.config.ctx.name.clone()
}
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, lifetime: TokenStream) -> TokenStream {
match b {
BuiltinType::String => quote!(wiggle::GuestPtr<#lifetime, str>),
BuiltinType::U8 => quote!(u8),
BuiltinType::U16 => quote!(u16),
BuiltinType::U32 => quote!(u32),
BuiltinType::U64 => quote!(u64),
BuiltinType::S8 => quote!(i8),
BuiltinType::S16 => quote!(i16),
BuiltinType::S32 => quote!(i32),
BuiltinType::S64 => quote!(i64),
BuiltinType::F32 => quote!(f32),
BuiltinType::F64 => quote!(f64),
BuiltinType::Char8 => quote!(u8),
BuiltinType::USize => quote!(u32),
}
}
pub fn atom_type(&self, atom: AtomType) -> TokenStream {
match atom {
AtomType::I32 => quote!(i32),
AtomType::I64 => quote!(i64),
AtomType::F32 => quote!(f32),
AtomType::F64 => quote!(f64),
}
}
pub fn type_ref(&self, tref: &TypeRef, lifetime: TokenStream) -> TokenStream {
match tref {
TypeRef::Name(nt) => {
let ident = self.type_(&nt.name);
if nt.tref.needs_lifetime() {
quote!(#ident<#lifetime>)
} else {
quote!(#ident)
}
}
TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => self.builtin_type(*builtin, lifetime.clone()),
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = self.type_ref(&pointee, lifetime.clone());
quote!(wiggle::GuestPtr<#lifetime, #pointee_type>)
}
witx::Type::Array(pointee) => {
let pointee_type = self.type_ref(&pointee, lifetime.clone());
quote!(wiggle::GuestPtr<#lifetime, [#pointee_type]>)
}
_ => unimplemented!("anonymous type ref {:?}", tref),
},
}
}
pub fn enum_variant(&self, id: &Id) -> Ident {
// FIXME this is a hack - just a proof of concept.
if id.as_str().starts_with('2') {
format_ident!("TooBig")
} else if id.as_str() == "type" {
format_ident!("Type")
} else {
format_ident!("{}", id.as_str().to_camel_case())
}
}
pub fn flag_member(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_shouty_snake_case())
}
pub fn int_member(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_shouty_snake_case())
}
pub fn struct_member(&self, id: &Id) -> Ident {
// FIXME this is a hack - just a proof of concept.
if id.as_str() == "type" {
format_ident!("type_")
} else {
format_ident!("{}", id.as_str().to_snake_case())
}
}
pub fn module(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_snake_case())
}
pub fn trait_name(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_camel_case())
}
pub fn func(&self, id: &Id) -> Ident {
format_ident!("{}", id.as_str().to_snake_case())
}
pub fn func_param(&self, id: &Id) -> Ident {
// FIXME this is a hack - just a proof of concept.
if id.as_str() == "in" {
format_ident!("in_")
} else {
format_ident!("{}", id.as_str().to_snake_case())
}
}
pub fn func_core_arg(&self, arg: &witx::CoreParamType) -> Ident {
match arg.signifies {
witx::CoreParamSignifies::Value { .. } => self.func_param(&arg.param.name),
witx::CoreParamSignifies::PointerTo => self.func_ptr_binding(&arg.param.name),
witx::CoreParamSignifies::LengthOf => self.func_len_binding(&arg.param.name),
}
}
/// For when you need a {name}_ptr binding for passing a value by reference:
pub fn func_ptr_binding(&self, id: &Id) -> Ident {
format_ident!("{}_ptr", id.as_str().to_snake_case())
}
/// For when you need a {name}_len binding for passing an array:
pub fn func_len_binding(&self, id: &Id) -> Ident {
format_ident!("{}_len", id.as_str().to_snake_case())
}
}

View File

@@ -0,0 +1,113 @@
use super::{atom_token, int_repr_tokens};
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
let ident = names.type_(&name);
let repr = int_repr_tokens(e.repr);
let abi_repr = atom_token(match e.repr {
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
witx::IntRepr::U64 => witx::AtomType::I64,
});
let mut variant_names = vec![];
let mut tryfrom_repr_cases = vec![];
let mut to_repr_cases = vec![];
let mut to_display = vec![];
for (n, variant) in e.variants.iter().enumerate() {
let variant_name = names.enum_variant(&variant.name);
let docs = variant.docs.trim();
let ident_str = ident.to_string();
let variant_str = variant_name.to_string();
tryfrom_repr_cases.push(quote!(#n => Ok(#ident::#variant_name)));
to_repr_cases.push(quote!(#ident::#variant_name => #n as #repr));
to_display.push(quote!(#ident::#variant_name => format!("{} ({}::{}({}))", #docs, #ident_str, #variant_str, #repr::from(*self))));
variant_names.push(variant_name);
}
quote! {
#[repr(#repr)]
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
pub enum #ident {
#(#variant_names),*
}
impl ::std::fmt::Display for #ident {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let to_str = match self {
#(#to_display,)*
};
write!(f, "{}", to_str)
}
}
impl ::std::convert::TryFrom<#repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #repr) -> Result<#ident, wiggle::GuestError> {
match value as usize {
#(#tryfrom_repr_cases),*,
_ => Err(wiggle::GuestError::InvalidEnumValue(stringify!(#ident))),
}
}
}
impl ::std::convert::TryFrom<#abi_repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
#ident::try_from(value as #repr)
}
}
impl From<#ident> for #repr {
fn from(e: #ident) -> #repr {
match e {
#(#to_repr_cases),*
}
}
}
impl From<#ident> for #abi_repr {
fn from(e: #ident) -> #abi_repr {
#repr::from(e) as #abi_repr
}
}
impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 {
#repr::guest_size()
}
fn guest_align() -> usize {
#repr::guest_align()
}
fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> {
use std::convert::TryFrom;
let reprval = #repr::read(&location.cast())?;
let value = #ident::try_from(reprval)?;
Ok(value)
}
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self)
-> Result<(), wiggle::GuestError>
{
#repr::write(&location.cast(), #repr::from(val))
}
}
unsafe impl <'a> wiggle::GuestTypeTransparent<'a> for #ident {
#[inline]
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
use std::convert::TryFrom;
// Validate value in memory using #ident::try_from(reprval)
let reprval = unsafe { (location as *mut #repr).read() };
let _val = #ident::try_from(reprval)?;
Ok(())
}
}
}
}

View File

@@ -0,0 +1,181 @@
use super::{atom_token, int_repr_tokens};
use crate::names::Names;
use proc_macro2::{Literal, TokenStream};
use quote::quote;
use std::convert::TryFrom;
pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream {
let ident = names.type_(&name);
let repr = int_repr_tokens(f.repr);
let abi_repr = atom_token(match f.repr {
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
witx::IntRepr::U64 => witx::AtomType::I64,
});
let mut names_ = vec![];
let mut values_ = vec![];
for (i, f) in f.flags.iter().enumerate() {
let name = names.flag_member(&f.name);
let value = 1u128
.checked_shl(u32::try_from(i).expect("flag value overflow"))
.expect("flag value overflow");
let value_token = Literal::u128_unsuffixed(value);
names_.push(name);
values_.push(value_token);
}
quote! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
pub struct #ident(#repr);
impl #ident {
#(pub const #names_: #ident = #ident(#values_);)*
#[inline]
pub const fn empty() -> Self {
#ident(0)
}
#[inline]
pub const fn all() -> Self {
#ident(#(#values_)|*)
}
#[inline]
pub fn contains(&self, other: &#ident) -> bool {
!*self & *other == Self::empty()
}
}
impl ::std::fmt::Display for #ident {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let mut first = true;
#(
if self.0 & #values_ == #values_ {
if !first {
f.write_str("|")?;
}
first = false;
f.write_fmt(format_args!("{}", stringify!(#names_).to_lowercase()))?;
}
)*
if first {
f.write_str("empty")?;
}
f.write_fmt(format_args!(" ({:#x})", self.0))?;
Ok(())
}
}
impl ::std::ops::BitAnd for #ident {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
#ident(self.0 & rhs.0)
}
}
impl ::std::ops::BitAndAssign for #ident {
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs
}
}
impl ::std::ops::BitOr for #ident {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
#ident(self.0 | rhs.0)
}
}
impl ::std::ops::BitOrAssign for #ident {
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs
}
}
impl ::std::ops::BitXor for #ident {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
#ident(self.0 ^ rhs.0)
}
}
impl ::std::ops::BitXorAssign for #ident {
fn bitxor_assign(&mut self, rhs: Self) {
*self = *self ^ rhs
}
}
impl ::std::ops::Not for #ident {
type Output = Self;
fn not(self) -> Self::Output {
#ident(!self.0)
}
}
impl ::std::convert::TryFrom<#repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #repr) -> Result<Self, wiggle::GuestError> {
if #repr::from(!#ident::all()) & value != 0 {
Err(wiggle::GuestError::InvalidFlagValue(stringify!(#ident)))
} else {
Ok(#ident(value))
}
}
}
impl ::std::convert::TryFrom<#abi_repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
#ident::try_from(value as #repr)
}
}
impl From<#ident> for #repr {
fn from(e: #ident) -> #repr {
e.0
}
}
impl From<#ident> for #abi_repr {
fn from(e: #ident) -> #abi_repr {
#repr::from(e) as #abi_repr
}
}
impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 {
#repr::guest_size()
}
fn guest_align() -> usize {
#repr::guest_align()
}
fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> {
use std::convert::TryFrom;
let reprval = #repr::read(&location.cast())?;
let value = #ident::try_from(reprval)?;
Ok(value)
}
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> {
let val: #repr = #repr::from(val);
#repr::write(&location.cast(), val)
}
}
unsafe impl <'a> wiggle::GuestTypeTransparent<'a> for #ident {
#[inline]
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
use std::convert::TryFrom;
// Validate value in memory using #ident::try_from(reprval)
let reprval = unsafe { (location as *mut #repr).read() };
let _val = #ident::try_from(reprval)?;
Ok(())
}
}
}
}

View File

@@ -0,0 +1,83 @@
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
use witx::Layout;
pub(super) fn define_handle(
names: &Names,
name: &witx::Id,
h: &witx::HandleDatatype,
) -> TokenStream {
let ident = names.type_(name);
let size = h.mem_size_align().size as u32;
let align = h.mem_size_align().align as usize;
quote! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
pub struct #ident(u32);
impl #ident {
pub unsafe fn inner(&self) -> u32 {
self.0
}
}
impl From<#ident> for u32 {
fn from(e: #ident) -> u32 {
e.0
}
}
impl From<#ident> for i32 {
fn from(e: #ident) -> i32 {
e.0 as i32
}
}
impl From<u32> for #ident {
fn from(e: u32) -> #ident {
#ident(e)
}
}
impl From<i32> for #ident {
fn from(e: i32) -> #ident {
#ident(e as u32)
}
}
impl ::std::fmt::Display for #ident {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}({})", stringify!(#ident), self.0)
}
}
impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 {
#size
}
fn guest_align() -> usize {
#align
}
fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> {
Ok(#ident(u32::read(&location.cast())?))
}
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
u32::write(&location.cast(), val.0)
}
}
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
#[inline]
fn validate(_location: *mut #ident) -> Result<(), wiggle::GuestError> {
// All bit patterns accepted
Ok(())
}
}
}
}

View File

@@ -0,0 +1,93 @@
use super::{atom_token, int_repr_tokens};
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype) -> TokenStream {
let ident = names.type_(&name);
let repr = int_repr_tokens(i.repr);
let abi_repr = atom_token(match i.repr {
witx::IntRepr::U8 | witx::IntRepr::U16 | witx::IntRepr::U32 => witx::AtomType::I32,
witx::IntRepr::U64 => witx::AtomType::I64,
});
let consts = i
.consts
.iter()
.map(|r#const| {
let const_ident = names.int_member(&r#const.name);
let value = r#const.value;
quote!(pub const #const_ident: #ident = #ident(#value))
})
.collect::<Vec<_>>();
quote! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
pub struct #ident(#repr);
impl #ident {
#(#consts;)*
}
impl ::std::fmt::Display for #ident {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl ::std::convert::TryFrom<#repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #repr) -> Result<Self, wiggle::GuestError> {
Ok(#ident(value))
}
}
impl ::std::convert::TryFrom<#abi_repr> for #ident {
type Error = wiggle::GuestError;
fn try_from(value: #abi_repr) -> Result<#ident, wiggle::GuestError> {
#ident::try_from(value as #repr)
}
}
impl From<#ident> for #repr {
fn from(e: #ident) -> #repr {
e.0
}
}
impl From<#ident> for #abi_repr {
fn from(e: #ident) -> #abi_repr {
#repr::from(e) as #abi_repr
}
}
impl<'a> wiggle::GuestType<'a> for #ident {
fn guest_size() -> u32 {
#repr::guest_size()
}
fn guest_align() -> usize {
#repr::guest_align()
}
fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> {
Ok(#ident(#repr::read(&location.cast())?))
}
fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> {
#repr::write(&location.cast(), val.0)
}
}
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
#[inline]
fn validate(_location: *mut #ident) -> Result<(), wiggle::GuestError> {
// All bit patterns accepted
Ok(())
}
}
}
}

View File

@@ -0,0 +1,90 @@
mod r#enum;
mod flags;
mod handle;
mod int;
mod r#struct;
mod union;
use crate::lifetimes::LifetimeExt;
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStream {
match &namedtype.tref {
witx::TypeRef::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to),
witx::TypeRef::Value(v) => match &**v {
witx::Type::Enum(e) => r#enum::define_enum(names, &namedtype.name, &e),
witx::Type::Int(i) => int::define_int(names, &namedtype.name, &i),
witx::Type::Flags(f) => flags::define_flags(names, &namedtype.name, &f),
witx::Type::Struct(s) => r#struct::define_struct(names, &namedtype.name, &s),
witx::Type::Union(u) => union::define_union(names, &namedtype.name, &u),
witx::Type::Handle(h) => handle::define_handle(names, &namedtype.name, &h),
witx::Type::Builtin(b) => define_builtin(names, &namedtype.name, *b),
witx::Type::Pointer(p) => {
define_witx_pointer(names, &namedtype.name, quote!(wiggle::GuestPtr), p)
}
witx::Type::ConstPointer(p) => {
define_witx_pointer(names, &namedtype.name, quote!(wiggle::GuestPtr), p)
}
witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr),
},
}
}
fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenStream {
let ident = names.type_(name);
let rhs = names.type_(&to.name);
if to.tref.needs_lifetime() {
quote!(pub type #ident<'a> = #rhs<'a>;)
} else {
quote!(pub type #ident = #rhs;)
}
}
fn define_builtin(names: &Names, name: &witx::Id, builtin: witx::BuiltinType) -> TokenStream {
let ident = names.type_(name);
let built = names.builtin_type(builtin, quote!('a));
if builtin.needs_lifetime() {
quote!(pub type #ident<'a> = #built;)
} else {
quote!(pub type #ident = #built;)
}
}
fn define_witx_pointer(
names: &Names,
name: &witx::Id,
pointer_type: TokenStream,
pointee: &witx::TypeRef,
) -> TokenStream {
let ident = names.type_(name);
let pointee_type = names.type_ref(pointee, quote!('a));
quote!(pub type #ident<'a> = #pointer_type<'a, #pointee_type>;)
}
fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream {
let ident = names.type_(name);
let pointee_type = names.type_ref(arr_raw, quote!('a));
quote!(pub type #ident<'a> = wiggle::GuestPtr<'a, [#pointee_type]>;)
}
fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {
match int_repr {
witx::IntRepr::U8 => quote!(u8),
witx::IntRepr::U16 => quote!(u16),
witx::IntRepr::U32 => quote!(u32),
witx::IntRepr::U64 => quote!(u64),
}
}
fn atom_token(atom: witx::AtomType) -> TokenStream {
match atom {
witx::AtomType::I32 => quote!(i32),
witx::AtomType::I64 => quote!(i64),
witx::AtomType::F32 => quote!(f32),
witx::AtomType::F64 => quote!(f64),
}
}

View File

@@ -0,0 +1,134 @@
use crate::lifetimes::{anon_lifetime, LifetimeExt};
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
use witx::Layout;
pub(super) fn define_struct(
names: &Names,
name: &witx::Id,
s: &witx::StructDatatype,
) -> TokenStream {
let ident = names.type_(name);
let size = s.mem_size_align().size as u32;
let align = s.mem_size_align().align as usize;
let member_names = s.members.iter().map(|m| names.struct_member(&m.name));
let member_decls = s.members.iter().map(|m| {
let name = names.struct_member(&m.name);
let type_ = match &m.tref {
witx::TypeRef::Name(nt) => names.type_(&nt.name),
witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(&pointee, quote!('a));
quote!(wiggle::GuestPtr<'a, #pointee_type>)
}
_ => unimplemented!("other anonymous struct members"),
},
};
quote!(pub #name: #type_)
});
let member_reads = s.member_layout().into_iter().map(|ml| {
let name = names.struct_member(&ml.member.name);
let offset = ml.offset as u32;
let location = quote!(location.cast::<u8>().add(#offset)?.cast());
match &ml.member.tref {
witx::TypeRef::Name(nt) => {
let type_ = names.type_(&nt.name);
quote! {
let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
}
}
witx::TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => {
let type_ = names.builtin_type(*builtin, anon_lifetime());
quote! {
let #name = <#type_ as wiggle::GuestType>::read(&#location)?;
}
}
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(&pointee, anon_lifetime());
quote! {
let #name = <wiggle::GuestPtr::<#pointee_type> as wiggle::GuestType>::read(&#location)?;
}
}
_ => unimplemented!("other anonymous struct members"),
},
}
});
let member_writes = s.member_layout().into_iter().map(|ml| {
let name = names.struct_member(&ml.member.name);
let offset = ml.offset as u32;
quote! {
wiggle::GuestType::write(
&location.cast::<u8>().add(#offset)?.cast(),
val.#name,
)?;
}
});
let (struct_lifetime, extra_derive) = if s.needs_lifetime() {
(quote!(<'a>), quote!())
} else {
(quote!(), quote!(, PartialEq))
};
let transparent = if s.is_transparent() {
let member_validate = s.member_layout().into_iter().map(|ml| {
let offset = ml.offset;
let typename = names.type_ref(&ml.member.tref, anon_lifetime());
quote! {
// SAFETY: caller has validated bounds and alignment of `location`.
// member_layout gives correctly-aligned pointers inside that area.
#typename::validate(
unsafe { (location as *mut u8).add(#offset) as *mut _ }
)?;
}
});
quote! {
unsafe impl<'a> wiggle::GuestTypeTransparent<'a> for #ident {
#[inline]
fn validate(location: *mut #ident) -> Result<(), wiggle::GuestError> {
#(#member_validate)*
Ok(())
}
}
}
} else {
quote!()
};
quote! {
#[derive(Clone, Debug #extra_derive)]
pub struct #ident #struct_lifetime {
#(#member_decls),*
}
impl<'a> wiggle::GuestType<'a> for #ident #struct_lifetime {
fn guest_size() -> u32 {
#size
}
fn guest_align() -> usize {
#align
}
fn read(location: &wiggle::GuestPtr<'a, Self>) -> Result<Self, wiggle::GuestError> {
#(#member_reads)*
Ok(#ident { #(#member_names),* })
}
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> {
#(#member_writes)*
Ok(())
}
}
#transparent
}
}

View File

@@ -0,0 +1,109 @@
use crate::lifetimes::LifetimeExt;
use crate::names::Names;
use proc_macro2::TokenStream;
use quote::quote;
use witx::Layout;
pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream {
let ident = names.type_(name);
let size = u.mem_size_align().size as u32;
let align = u.mem_size_align().align as usize;
let ulayout = u.union_layout();
let contents_offset = ulayout.contents_offset as u32;
let lifetime = quote!('a);
let variants = u.variants.iter().map(|v| {
let var_name = names.enum_variant(&v.name);
if let Some(tref) = &v.tref {
let var_type = names.type_ref(&tref, lifetime.clone());
quote!(#var_name(#var_type))
} else {
quote!(#var_name)
}
});
let tagname = names.type_(&u.tag.name);
let read_variant = u.variants.iter().map(|v| {
let variantname = names.enum_variant(&v.name);
if let Some(tref) = &v.tref {
let varianttype = names.type_ref(tref, lifetime.clone());
quote! {
#tagname::#variantname => {
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
let variant_val = <#varianttype as wiggle::GuestType>::read(&variant_ptr.cast())?;
Ok(#ident::#variantname(variant_val))
}
}
} else {
quote! { #tagname::#variantname => Ok(#ident::#variantname), }
}
});
let write_variant = u.variants.iter().map(|v| {
let variantname = names.enum_variant(&v.name);
let write_tag = quote! {
location.cast().write(#tagname::#variantname)?;
};
if let Some(tref) = &v.tref {
let varianttype = names.type_ref(tref, lifetime.clone());
quote! {
#ident::#variantname(contents) => {
#write_tag
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
<#varianttype as wiggle::GuestType>::write(&variant_ptr.cast(), contents)?;
}
}
} else {
quote! {
#ident::#variantname => {
#write_tag
}
}
}
});
let (enum_lifetime, extra_derive) = if u.needs_lifetime() {
(quote!(<'a>), quote!())
} else {
(quote!(), quote!(, PartialEq))
};
quote! {
#[derive(Clone, Debug #extra_derive)]
pub enum #ident #enum_lifetime {
#(#variants),*
}
impl<'a> wiggle::GuestType<'a> for #ident #enum_lifetime {
fn guest_size() -> u32 {
#size
}
fn guest_align() -> usize {
#align
}
fn read(location: &wiggle::GuestPtr<'a, Self>)
-> Result<Self, wiggle::GuestError>
{
let tag = location.cast().read()?;
match tag {
#(#read_variant)*
}
}
fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self)
-> Result<(), wiggle::GuestError>
{
match val {
#(#write_variant)*
}
Ok(())
}
}
}
}