Start drafting out flags datatype generation (#11)
* Adds support for flags datatype This commit adds support for `FlagsDatatype`. In WASI, flags are represented as bitfields/bitflags, therefore, it is important for the type to have bitwise operations implemented, and some way of checking whether the current set of flags contains some other set (i.e., they intersect). Thus, this commit automatically derives `BitAnd`, etc. for the derived flags datatype. It also automatically provides an `ALL_FLAGS` value which corresponds to a bitwise-or of all flag values present and is provided for convenience. * Simplify read_from_guest
This commit is contained in:
@@ -153,6 +153,7 @@ fn marshal_arg(
|
||||
|
||||
match &*tref.type_() {
|
||||
witx::Type::Enum(_e) => try_into_conversion,
|
||||
witx::Type::Flags(_f) => try_into_conversion,
|
||||
witx::Type::Builtin(b) => match b {
|
||||
witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => {
|
||||
try_into_conversion
|
||||
@@ -329,7 +330,7 @@ where
|
||||
| witx::BuiltinType::Char8 => write_val_to_ptr,
|
||||
witx::BuiltinType::String => unimplemented!("string types"),
|
||||
},
|
||||
witx::Type::Enum(_e) => write_val_to_ptr,
|
||||
witx::Type::Enum(_) | witx::Type::Flags(_) => write_val_to_ptr,
|
||||
_ => unimplemented!("marshal result"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use heck::{CamelCase, SnakeCase};
|
||||
use heck::{CamelCase, ShoutySnakeCase, SnakeCase};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use witx::{AtomType, BuiltinType, Id, TypeRef};
|
||||
@@ -82,6 +82,10 @@ impl Names {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flag_member(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_shouty_snake_case())
|
||||
}
|
||||
|
||||
pub fn struct_member(&self, id: &Id) -> Ident {
|
||||
format_ident!("{}", id.as_str().to_snake_case())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::names::Names;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
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 {
|
||||
@@ -10,7 +11,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea
|
||||
witx::TypeRef::Value(v) => match &**v {
|
||||
witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e),
|
||||
witx::Type::Int(_) => unimplemented!("int types"),
|
||||
witx::Type::Flags(_) => unimplemented!("flag types"),
|
||||
witx::Type::Flags(f) => define_flags(names, &namedtype.name, &f),
|
||||
witx::Type::Struct(s) => {
|
||||
if struct_is_copy(s) {
|
||||
define_copy_struct(names, &namedtype.name, &s)
|
||||
@@ -45,6 +46,151 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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::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 wiggle_runtime::GuestType 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<'a>(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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl wiggle_runtime::GuestTypeCopy for #ident {}
|
||||
impl wiggle_runtime::GuestTypeClone for #ident {
|
||||
fn read_from_guest(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||
Ok(*location.as_ref()?)
|
||||
}
|
||||
fn write_to_guest(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
||||
let val: #repr = #repr::from(*self);
|
||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenStream {
|
||||
let ident = names.type_(&name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user