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:
Jakub Konka
2020-02-21 09:58:43 +01:00
committed by GitHub
parent 898af8e2fb
commit 3ef24a04fe
6 changed files with 261 additions and 4 deletions

View File

@@ -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"),
}
}

View File

@@ -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())
}

View File

@@ -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);