Split wiggle-generate type generation into modules
This commit splits `generate/src/types.rs` into submodules, each responsible for specifying a (compound) type. `generate/src/types.rs` grew in size a lot, and IMHO this should yield readability and maintenance.
This commit is contained in:
@@ -1,915 +0,0 @@
|
||||
use crate::lifetimes::{anon_lifetime, LifetimeExt};
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
use std::convert::TryFrom;
|
||||
use witx::Layout;
|
||||
|
||||
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) => define_enum(names, &namedtype.name, &e),
|
||||
witx::Type::Int(i) => define_int(names, &namedtype.name, &i),
|
||||
witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f),
|
||||
witx::Type::Struct(s) => {
|
||||
if !s.needs_lifetime() {
|
||||
define_copy_struct(names, &namedtype.name, &s)
|
||||
} else {
|
||||
define_ptr_struct(names, &namedtype.name, &s)
|
||||
}
|
||||
}
|
||||
witx::Type::Union(u) => define_union(names, &namedtype.name, &u),
|
||||
witx::Type::Handle(h) => 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_runtime::GuestPtrMut),
|
||||
p,
|
||||
),
|
||||
witx::Type::ConstPointer(p) => {
|
||||
define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::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_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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle_runtime::GuestError> {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
Ok(*location.as_ref()?)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
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 flag_constructors = vec![];
|
||||
let mut all_values = 0;
|
||||
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);
|
||||
flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token)));
|
||||
all_values += value;
|
||||
}
|
||||
let all_values_token = Literal::u128_unsuffixed(all_values);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(#repr);
|
||||
|
||||
impl #ident {
|
||||
#(#flag_constructors);*;
|
||||
pub const ALL_FLAGS: #ident = #ident(#all_values_token);
|
||||
|
||||
pub fn contains(&self, other: &#ident) -> bool {
|
||||
#repr::from(!*self & *other) == 0 as #repr
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "{}({:#b})", #ident_str, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle_runtime::GuestError> {
|
||||
if #repr::from(!#ident::ALL_FLAGS) & value != 0 {
|
||||
Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident)))
|
||||
} else {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
Ok(*location.as_ref()?)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
match value as usize {
|
||||
#(#tryfrom_repr_cases),*,
|
||||
_ => Err(wiggle_runtime::GuestError::InvalidEnumValue(stringify!(#ident))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
// Perform validation as part of as_ref:
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
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 u32;
|
||||
quote! {
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(u32);
|
||||
|
||||
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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
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_copy_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 u32;
|
||||
|
||||
let member_decls = s.members.iter().map(|m| {
|
||||
let name = names.struct_member(&m.name);
|
||||
let type_ = names.type_ref(&m.tref, anon_lifetime());
|
||||
quote!(pub #name: #type_)
|
||||
});
|
||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
||||
let type_ = names.type_ref(&ml.member.tref, anon_lifetime());
|
||||
let offset = ml.offset as u32;
|
||||
let fieldname = names.struct_member(&ml.member.name);
|
||||
quote! {
|
||||
#type_::validate(
|
||||
&ptr.cast(#offset).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField{
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?
|
||||
).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?;
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident {
|
||||
#(#member_decls),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#(#member_valids)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
fn define_ptr_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 u32;
|
||||
|
||||
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) => {
|
||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||
quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>)
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||
quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>)
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
};
|
||||
quote!(pub #name: #type_)
|
||||
});
|
||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
||||
let type_ = match &ml.member.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) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>)
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote!(wiggle_runtime::GuestPtr::<#pointee_type>)
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
};
|
||||
let offset = ml.offset as u32;
|
||||
let fieldname = names.struct_member(&ml.member.name);
|
||||
quote! {
|
||||
#type_::validate(
|
||||
&ptr.cast(#offset).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField{
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?
|
||||
).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?;
|
||||
}
|
||||
});
|
||||
|
||||
let member_reads = s.member_layout().into_iter().map(|ml| {
|
||||
let name = names.struct_member(&ml.member.name);
|
||||
let offset = ml.offset as u32;
|
||||
match &ml.member.tref {
|
||||
witx::TypeRef::Name(nt) => {
|
||||
let type_ = names.type_(&nt.name);
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::TypeRef::Value(ty) => match &**ty {
|
||||
witx::Type::Builtin(builtin) => {
|
||||
let type_ = names.builtin_type(*builtin, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::Type::Pointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <wiggle_runtime::GuestPtrMut::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <wiggle_runtime::GuestPtr::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
_ => 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! {
|
||||
self.#name.write(&location.cast(#offset).expect("cast to inner member"));
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(Clone)]
|
||||
pub struct #ident<'a> {
|
||||
#(#member_decls),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#(#member_valids)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> {
|
||||
#(#member_reads)*
|
||||
Ok(#ident { #(#member_names),* })
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
#(#member_writes)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn union_validate(
|
||||
names: &Names,
|
||||
typename: TokenStream,
|
||||
u: &witx::UnionDatatype,
|
||||
ulayout: &witx::UnionLayout,
|
||||
) -> TokenStream {
|
||||
let tagname = names.type_(&u.tag.name);
|
||||
let contents_offset = ulayout.contents_offset as u32;
|
||||
|
||||
let with_err = |f: &str| -> TokenStream {
|
||||
quote!(|e| wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#typename).to_owned(),
|
||||
field: #f.to_owned(),
|
||||
err: Box::new(e),
|
||||
})
|
||||
};
|
||||
|
||||
let tag_err = with_err("<tag>");
|
||||
let variant_validation = u.variants.iter().map(|v| {
|
||||
let err = with_err(v.name.as_str());
|
||||
let variantname = names.enum_variant(&v.name);
|
||||
if let Some(tref) = &v.tref {
|
||||
let lifetime = anon_lifetime();
|
||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||
quote! {
|
||||
#tagname::#variantname => {
|
||||
let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?;
|
||||
<#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { #tagname::#variantname => {} }
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?;
|
||||
match tag {
|
||||
#(#variant_validation)*
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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 u32;
|
||||
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::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
||||
let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?;
|
||||
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! {
|
||||
let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report");
|
||||
let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report");
|
||||
*tag_ref = #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::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
||||
<#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#ident::#variantname => {
|
||||
#write_tag
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let validate = union_validate(names, ident.clone(), u, &ulayout);
|
||||
|
||||
if !u.needs_lifetime() {
|
||||
// Type does not have a lifetime parameter:
|
||||
quote! {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum #ident {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#validate
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>)
|
||||
-> Result<Self, wiggle_runtime::GuestError> {
|
||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
||||
match tag {
|
||||
#(#read_variant)*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) {
|
||||
match self {
|
||||
#(#write_variant)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(Clone)]
|
||||
pub enum #ident<#lifetime> {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#validate
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>)
|
||||
-> Result<Self, wiggle_runtime::GuestError> {
|
||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
||||
match tag {
|
||||
#(#read_variant)*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) {
|
||||
match self {
|
||||
#(#write_variant)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_runtime::GuestArray<'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),
|
||||
}
|
||||
}
|
||||
113
crates/generate/src/types/enum.rs
Normal file
113
crates/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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
match value as usize {
|
||||
#(#tryfrom_repr_cases),*,
|
||||
_ => Err(wiggle_runtime::GuestError::InvalidEnumValue(stringify!(#ident))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
// Perform validation as part of as_ref:
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
159
crates/generate/src/types/flags.rs
Normal file
159
crates/generate/src/types/flags.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
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 flag_constructors = vec![];
|
||||
let mut all_values = 0;
|
||||
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);
|
||||
flag_constructors.push(quote!(pub const #name: #ident = #ident(#value_token)));
|
||||
all_values += value;
|
||||
}
|
||||
let all_values_token = Literal::u128_unsuffixed(all_values);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(#repr);
|
||||
|
||||
impl #ident {
|
||||
#(#flag_constructors);*;
|
||||
pub const ALL_FLAGS: #ident = #ident(#all_values_token);
|
||||
|
||||
pub fn contains(&self, other: &#ident) -> bool {
|
||||
#repr::from(!*self & *other) == 0 as #repr
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "{}({:#b})", #ident_str, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle_runtime::GuestError> {
|
||||
if #repr::from(!#ident::ALL_FLAGS) & value != 0 {
|
||||
Err(wiggle_runtime::GuestError::InvalidFlagValue(stringify!(#ident)))
|
||||
} else {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
Ok(*location.as_ref()?)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
77
crates/generate/src/types/handle.rs
Normal file
77
crates/generate/src/types/handle.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
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 u32;
|
||||
quote! {
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(u32);
|
||||
|
||||
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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
97
crates/generate/src/types/int.rs
Normal file
97
crates/generate/src/types/int.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
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_runtime::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, wiggle_runtime::GuestError> {
|
||||
Ok(#ident(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#abi_repr> for #ident {
|
||||
type Error = wiggle_runtime::GuestError;
|
||||
fn try_from(value: #abi_repr) -> Result<#ident, wiggle_runtime::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_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
::std::mem::size_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
::std::mem::align_of::<#repr>() as u32
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
use ::std::convert::TryFrom;
|
||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
||||
let _ = #ident::try_from(raw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
Ok(*location.as_ref()?)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
93
crates/generate/src/types/mod.rs
Normal file
93
crates/generate/src/types/mod.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
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_runtime::GuestPtrMut),
|
||||
p,
|
||||
),
|
||||
witx::Type::ConstPointer(p) => {
|
||||
define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::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_runtime::GuestArray<'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),
|
||||
}
|
||||
}
|
||||
226
crates/generate/src/types/struct.rs
Normal file
226
crates/generate/src/types/struct.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
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 {
|
||||
if !s.needs_lifetime() {
|
||||
define_copy_struct(names, name, s)
|
||||
} else {
|
||||
define_ptr_struct(names, name, s)
|
||||
}
|
||||
}
|
||||
|
||||
fn define_copy_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 u32;
|
||||
|
||||
let member_decls = s.members.iter().map(|m| {
|
||||
let name = names.struct_member(&m.name);
|
||||
let type_ = names.type_ref(&m.tref, anon_lifetime());
|
||||
quote!(pub #name: #type_)
|
||||
});
|
||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
||||
let type_ = names.type_ref(&ml.member.tref, anon_lifetime());
|
||||
let offset = ml.offset as u32;
|
||||
let fieldname = names.struct_member(&ml.member.name);
|
||||
quote! {
|
||||
#type_::validate(
|
||||
&ptr.cast(#offset).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField{
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?
|
||||
).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?;
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident {
|
||||
#(#member_decls),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#(#member_valids)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
let r = location.as_ref()?;
|
||||
Ok(*r)
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
||||
}
|
||||
}
|
||||
|
||||
fn define_ptr_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 u32;
|
||||
|
||||
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) => {
|
||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||
quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>)
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||
quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>)
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
};
|
||||
quote!(pub #name: #type_)
|
||||
});
|
||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
||||
let type_ = match &ml.member.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) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>)
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote!(wiggle_runtime::GuestPtr::<#pointee_type>)
|
||||
}
|
||||
_ => unimplemented!("other anonymous struct members"),
|
||||
},
|
||||
};
|
||||
let offset = ml.offset as u32;
|
||||
let fieldname = names.struct_member(&ml.member.name);
|
||||
quote! {
|
||||
#type_::validate(
|
||||
&ptr.cast(#offset).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField{
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?
|
||||
).map_err(|e|
|
||||
wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#ident).to_owned(),
|
||||
field: stringify!(#fieldname).to_owned(),
|
||||
err: Box::new(e),
|
||||
})?;
|
||||
}
|
||||
});
|
||||
|
||||
let member_reads = s.member_layout().into_iter().map(|ml| {
|
||||
let name = names.struct_member(&ml.member.name);
|
||||
let offset = ml.offset as u32;
|
||||
match &ml.member.tref {
|
||||
witx::TypeRef::Name(nt) => {
|
||||
let type_ = names.type_(&nt.name);
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::TypeRef::Value(ty) => match &**ty {
|
||||
witx::Type::Builtin(builtin) => {
|
||||
let type_ = names.builtin_type(*builtin, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::Type::Pointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <wiggle_runtime::GuestPtrMut::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
witx::Type::ConstPointer(pointee) => {
|
||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||
quote! {
|
||||
let #name = <wiggle_runtime::GuestPtr::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
||||
}
|
||||
}
|
||||
_ => 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! {
|
||||
self.#name.write(&location.cast(#offset).expect("cast to inner member"));
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(Clone)]
|
||||
pub struct #ident<'a> {
|
||||
#(#member_decls),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#(#member_valids)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> {
|
||||
#(#member_reads)*
|
||||
Ok(#ident { #(#member_names),* })
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
||||
#(#member_writes)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
199
crates/generate/src/types/union.rs
Normal file
199
crates/generate/src/types/union.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
use crate::lifetimes::{anon_lifetime, 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 u32;
|
||||
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::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
||||
let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?;
|
||||
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! {
|
||||
let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report");
|
||||
let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report");
|
||||
*tag_ref = #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::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
||||
<#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#ident::#variantname => {
|
||||
#write_tag
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let validate = union_validate(names, ident.clone(), u, &ulayout);
|
||||
|
||||
if !u.needs_lifetime() {
|
||||
// Type does not have a lifetime parameter:
|
||||
quote! {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum #ident {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#validate
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>)
|
||||
-> Result<Self, wiggle_runtime::GuestError> {
|
||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
||||
match tag {
|
||||
#(#read_variant)*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) {
|
||||
match self {
|
||||
#(#write_variant)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(Clone)]
|
||||
pub enum #ident<#lifetime> {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> {
|
||||
fn size() -> u32 {
|
||||
#size
|
||||
}
|
||||
|
||||
fn align() -> u32 {
|
||||
#align
|
||||
}
|
||||
|
||||
fn name() -> String {
|
||||
stringify!(#ident).to_owned()
|
||||
}
|
||||
|
||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> {
|
||||
#validate
|
||||
}
|
||||
|
||||
fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>)
|
||||
-> Result<Self, wiggle_runtime::GuestError> {
|
||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
||||
match tag {
|
||||
#(#read_variant)*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) {
|
||||
match self {
|
||||
#(#write_variant)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn union_validate(
|
||||
names: &Names,
|
||||
typename: TokenStream,
|
||||
u: &witx::UnionDatatype,
|
||||
ulayout: &witx::UnionLayout,
|
||||
) -> TokenStream {
|
||||
let tagname = names.type_(&u.tag.name);
|
||||
let contents_offset = ulayout.contents_offset as u32;
|
||||
|
||||
let with_err = |f: &str| -> TokenStream {
|
||||
quote!(|e| wiggle_runtime::GuestError::InDataField {
|
||||
typename: stringify!(#typename).to_owned(),
|
||||
field: #f.to_owned(),
|
||||
err: Box::new(e),
|
||||
})
|
||||
};
|
||||
|
||||
let tag_err = with_err("<tag>");
|
||||
let variant_validation = u.variants.iter().map(|v| {
|
||||
let err = with_err(v.name.as_str());
|
||||
let variantname = names.enum_variant(&v.name);
|
||||
if let Some(tref) = &v.tref {
|
||||
let lifetime = anon_lifetime();
|
||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||
quote! {
|
||||
#tagname::#variantname => {
|
||||
let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?;
|
||||
<#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { #tagname::#variantname => {} }
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?;
|
||||
match tag {
|
||||
#(#variant_validation)*
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user