Draft out IntDatatype in wiggle-generate (#15)
* 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.
This commit is contained in:
@@ -154,6 +154,7 @@ fn marshal_arg(
|
|||||||
match &*tref.type_() {
|
match &*tref.type_() {
|
||||||
witx::Type::Enum(_e) => try_into_conversion,
|
witx::Type::Enum(_e) => try_into_conversion,
|
||||||
witx::Type::Flags(_f) => try_into_conversion,
|
witx::Type::Flags(_f) => try_into_conversion,
|
||||||
|
witx::Type::Int(_i) => try_into_conversion,
|
||||||
witx::Type::Builtin(b) => match b {
|
witx::Type::Builtin(b) => match b {
|
||||||
witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => {
|
witx::BuiltinType::U8 | witx::BuiltinType::U16 | witx::BuiltinType::Char8 => {
|
||||||
try_into_conversion
|
try_into_conversion
|
||||||
@@ -359,7 +360,7 @@ where
|
|||||||
| witx::BuiltinType::Char8 => write_val_to_ptr,
|
| witx::BuiltinType::Char8 => write_val_to_ptr,
|
||||||
witx::BuiltinType::String => unimplemented!("string types"),
|
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"),
|
_ => unimplemented!("marshal result"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ impl Names {
|
|||||||
format_ident!("{}", id.as_str().to_shouty_snake_case())
|
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 {
|
pub fn struct_member(&self, id: &Id) -> Ident {
|
||||||
format_ident!("{}", id.as_str().to_snake_case())
|
format_ident!("{}", id.as_str().to_snake_case())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::Name(alias_to) => define_alias(names, &namedtype.name, &alias_to),
|
||||||
witx::TypeRef::Value(v) => match &**v {
|
witx::TypeRef::Value(v) => match &**v {
|
||||||
witx::Type::Enum(e) => define_enum(names, &namedtype.name, &e),
|
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::Flags(f) => define_flags(names, &namedtype.name, &f),
|
||||||
witx::Type::Struct(s) => {
|
witx::Type::Struct(s) => {
|
||||||
if struct_is_copy(s) {
|
if struct_is_copy(s) {
|
||||||
@@ -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 {
|
fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDatatype) -> TokenStream {
|
||||||
let ident = names.type_(&name);
|
let ident = names.type_(&name);
|
||||||
let repr = int_repr_tokens(f.repr);
|
let repr = int_repr_tokens(f.repr);
|
||||||
|
|||||||
@@ -143,6 +143,15 @@ impl foo::Foo for WasiCtx {
|
|||||||
println!("a_string='{}'", as_str);
|
println!("a_string='{}'", as_str);
|
||||||
Ok(as_str.len() as u32)
|
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
|
// Errno is used as a first return value in the functions above, therefore
|
||||||
// it must implement GuestErrorType with type Context = WasiCtx.
|
// it must implement GuestErrorType with type Context = WasiCtx.
|
||||||
@@ -958,3 +967,62 @@ proptest! {
|
|||||||
e.test()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
$awd
|
$awd
|
||||||
$suv))
|
$suv))
|
||||||
|
|
||||||
|
(typename $cookie
|
||||||
|
(int u64
|
||||||
|
(const $start 0)))
|
||||||
|
|
||||||
|
(typename $bool
|
||||||
|
(enum u8
|
||||||
|
$false
|
||||||
|
$true))
|
||||||
|
|
||||||
(typename $pair_ints
|
(typename $pair_ints
|
||||||
(struct
|
(struct
|
||||||
(field $first s32)
|
(field $first s32)
|
||||||
@@ -77,4 +86,9 @@
|
|||||||
(result $error $errno)
|
(result $error $errno)
|
||||||
(result $total_bytes u32)
|
(result $total_bytes u32)
|
||||||
)
|
)
|
||||||
|
(@interface func (export "cookie_cutter")
|
||||||
|
(param $init_cookie $cookie)
|
||||||
|
(result $error $errno)
|
||||||
|
(result $is_start $bool)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user