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:
117
crates/wiggle/generate/src/config.rs
Normal file
117
crates/wiggle/generate/src/config.rs
Normal 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()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
264
crates/wiggle/generate/src/funcs.rs
Normal file
264
crates/wiggle/generate/src/funcs.rs
Normal 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(¶m.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 = ¶m.tref;
|
||||
let interface_typename = names.type_ref(&tref, anon_lifetime());
|
||||
|
||||
let try_into_conversion = {
|
||||
let name = names.func_param(¶m.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(¶m.name);
|
||||
let name = names.func_param(¶m.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(¶m.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(¶m.name);
|
||||
quote! {
|
||||
let #name = #name as #interface_typename;
|
||||
}
|
||||
}
|
||||
witx::BuiltinType::String => {
|
||||
let lifetime = anon_lifetime();
|
||||
let ptr_name = names.func_ptr_binding(¶m.name);
|
||||
let len_name = names.func_len_binding(¶m.name);
|
||||
let name = names.func_param(¶m.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(¶m.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(¶m.name);
|
||||
let len_name = names.func_len_binding(¶m.name);
|
||||
let name = names.func_param(¶m.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(¶m.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,
|
||||
}
|
||||
}
|
||||
59
crates/wiggle/generate/src/lib.rs
Normal file
59
crates/wiggle/generate/src/lib.rs
Normal 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
|
||||
)
|
||||
}
|
||||
83
crates/wiggle/generate/src/lifetimes.rs
Normal file
83
crates/wiggle/generate/src/lifetimes.rs
Normal 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!('_)
|
||||
}
|
||||
72
crates/wiggle/generate/src/module_trait.rs
Normal file
72
crates/wiggle/generate/src/module_trait.rs
Normal 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)*
|
||||
}
|
||||
}
|
||||
}
|
||||
144
crates/wiggle/generate/src/names.rs
Normal file
144
crates/wiggle/generate/src/names.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
113
crates/wiggle/generate/src/types/enum.rs
Normal file
113
crates/wiggle/generate/src/types/enum.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
crates/wiggle/generate/src/types/flags.rs
Normal file
181
crates/wiggle/generate/src/types/flags.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
crates/wiggle/generate/src/types/handle.rs
Normal file
83
crates/wiggle/generate/src/types/handle.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
93
crates/wiggle/generate/src/types/int.rs
Normal file
93
crates/wiggle/generate/src/types/int.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
90
crates/wiggle/generate/src/types/mod.rs
Normal file
90
crates/wiggle/generate/src/types/mod.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
134
crates/wiggle/generate/src/types/struct.rs
Normal file
134
crates/wiggle/generate/src/types/struct.rs
Normal 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
|
||||
}
|
||||
}
|
||||
109
crates/wiggle/generate/src/types/union.rs
Normal file
109
crates/wiggle/generate/src/types/union.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user