Skip to content

Commit

Permalink
Draft out IntDatatype in wiggle-generate (bytecodealliance#15)
Browse files Browse the repository at this point in the history
* Draft out IntDatatype in wiggle-generate

This commit drafts out basic layout for `IntDatatype` structure in
`wiggle`. As it currently stands, an `Int` type is represented as
a one-element tuple struct much like `FlagDatatype`, however, with
this difference that we do not perform any checks on the input
underlying representation since any value for the prescribed type
is legal.

* Finish drafting IntDatatype support in wiggle

This commit adds necessary marshal stubs to properly pass `IntDatatype`
in and out of interface functions. It also adds a basic proptest.
  • Loading branch information
kubkon authored Feb 21, 2020
1 parent 6ab3ff7 commit f48474b
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 2 deletions.
3 changes: 2 additions & 1 deletion crates/generate/src/funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fn marshal_arg(
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
Expand Down Expand Up @@ -359,7 +360,7 @@ where
| witx::BuiltinType::Char8 => write_val_to_ptr,
witx::BuiltinType::String => unimplemented!("string types"),
},
witx::Type::Enum(_) | witx::Type::Flags(_) => write_val_to_ptr,
witx::Type::Enum(_) | witx::Type::Flags(_) | witx::Type::Int(_) => write_val_to_ptr,
_ => unimplemented!("marshal result"),
}
}
4 changes: 4 additions & 0 deletions crates/generate/src/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ impl Names {
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 {
format_ident!("{}", id.as_str().to_snake_case())
}
Expand Down
88 changes: 87 additions & 1 deletion crates/generate/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea
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(_) => unimplemented!("int types"),
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 struct_is_copy(s) {
Expand Down Expand Up @@ -46,6 +46,92 @@ fn define_alias(names: &Names, name: &witx::Id, to: &witx::NamedType) -> TokenSt
}
}

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::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 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<'a> wiggle_runtime::GuestTypeClone<'a> 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_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream {
let ident = names.type_(&name);
let repr = int_repr_tokens(f.repr);
Expand Down
68 changes: 68 additions & 0 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ impl foo::Foo for WasiCtx {
println!("a_string='{}'", as_str);
Ok(as_str.len() as u32)
}

fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result<types::Bool, types::Errno> {
let res = if init_cookie == types::Cookie::START {
types::Bool::True
} else {
types::Bool::False
};
Ok(res)
}
}
// Errno is used as a first return value in the functions above, therefore
// it must implement GuestErrorType with type Context = WasiCtx.
Expand Down Expand Up @@ -958,3 +967,62 @@ proptest! {
e.test()
}
}

fn cookie_strat() -> impl Strategy<Value = types::Cookie> {
(0..std::u64::MAX)
.prop_map(|x| types::Cookie::try_from(x).expect("within range of cookie"))
.boxed()
}

#[derive(Debug)]
struct CookieCutterExercise {
cookie: types::Cookie,
return_ptr_loc: MemArea,
}

impl CookieCutterExercise {
pub fn strat() -> BoxedStrategy<Self> {
(cookie_strat(), HostMemory::mem_area_strat(4))
.prop_map(|(cookie, return_ptr_loc)| Self {
cookie,
return_ptr_loc,
})
.boxed()
}

pub fn test(&self) {
let mut ctx = WasiCtx::new();
let mut host_memory = HostMemory::new();
let mut guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);

let res = foo::cookie_cutter(
&mut ctx,
&mut guest_memory,
self.cookie.into(),
self.return_ptr_loc.ptr as i32,
);
assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno");

let is_cookie_start = *guest_memory
.ptr::<types::Bool>(self.return_ptr_loc.ptr)
.expect("ptr to returned Bool")
.as_ref()
.expect("deref to Bool value");

assert_eq!(
if is_cookie_start == types::Bool::True {
true
} else {
false
},
self.cookie == types::Cookie::START,
"returned Bool should test if input was Cookie::START",
);
}
}
proptest! {
#[test]
fn cookie_cutter(e in CookieCutterExercise::strat()) {
e.test()
}
}
14 changes: 14 additions & 0 deletions tests/test.witx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
$awd
$suv))

(typename $cookie
(int u64
(const $start 0)))

(typename $bool
(enum u8
$false
$true))

(typename $pair_ints
(struct
(field $first s32)
Expand Down Expand Up @@ -77,4 +86,9 @@
(result $error $errno)
(result $total_bytes u32)
)
(@interface func (export "cookie_cutter")
(param $init_cookie $cookie)
(result $error $errno)
(result $is_start $bool)
)
)

0 comments on commit f48474b

Please sign in to comment.