diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index 7c8ef19b90..08d8bda414 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -82,7 +82,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { match param.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#name), witx::TypePassedBy::Pointer { .. } => quote!(&#name), - witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + witx::TypePassedBy::PointerLengthPair { .. } => quote!(&#name), } }); @@ -242,6 +242,36 @@ fn marshal_arg( }; } } + witx::Type::Array(arr) => { + let pointee_type = names.type_ref(arr, anon_lifetime()); + let ptr_name = names.func_ptr_binding(¶m.name); + let len_name = names.func_len_binding(¶m.name); + let name = names.func_param(¶m.name); + quote! { + let num_elems = match memory.ptr::(#len_name as u32) { + Ok(p) => match p.as_ref() { + Ok(r) => r, + Err(e) => { + #error_handling + } + } + Err(e) => { + #error_handling + } + }; + let #name = match memory.ptr::<#pointee_type>(#ptr_name as u32) { + Ok(p) => match p.array(*num_elems) { + Ok(s) => s, + Err(e) => { + #error_handling + } + } + Err(e) => { + #error_handling + } + }; + } + } _ => unimplemented!("argument type marshalling"), } } diff --git a/crates/generate/src/module_trait.rs b/crates/generate/src/module_trait.rs index c92fe85ffc..100144dca5 100644 --- a/crates/generate/src/module_trait.rs +++ b/crates/generate/src/module_trait.rs @@ -15,7 +15,7 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream { let arg_type = match arg.tref.type_().passed_by() { witx::TypePassedBy::Value { .. } => quote!(#arg_typename), witx::TypePassedBy::Pointer { .. } => quote!(&#arg_typename), - witx::TypePassedBy::PointerLengthPair { .. } => unimplemented!(), + witx::TypePassedBy::PointerLengthPair { .. } => quote!(&#arg_typename), }; quote!(#arg_name: #arg_type) }); diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index 10e34894db..7aa728f050 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -30,7 +30,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea witx::Type::ConstPointer(p) => { define_witx_pointer(names, &namedtype.name, quote!(wiggle_runtime::GuestPtr), p) } - witx::Type::Array { .. } => unimplemented!("array types"), + witx::Type::Array(arr) => define_witx_array(names, &namedtype.name, &arr), }, } } @@ -156,7 +156,7 @@ pub fn type_needs_lifetime(tref: &witx::TypeRef) -> bool { witx::Type::Struct(s) => !struct_is_copy(&s), witx::Type::Union { .. } => true, witx::Type::Pointer { .. } | witx::Type::ConstPointer { .. } => true, - witx::Type::Array { .. } => unimplemented!(), + witx::Type::Array { .. } => true, } } @@ -380,6 +380,12 @@ fn define_witx_pointer( 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), diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 6f2b17ecfc..5c3cea5e70 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -6,5 +6,5 @@ mod region; pub use error::GuestError; pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr}; -pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; +pub use memory::{GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; pub use region::Region; diff --git a/crates/runtime/src/memory/array.rs b/crates/runtime/src/memory/array.rs index 2ba08587b7..2250497a3e 100644 --- a/crates/runtime/src/memory/array.rs +++ b/crates/runtime/src/memory/array.rs @@ -1,9 +1,6 @@ -use super::ptr::{GuestPtr, GuestPtrMut, GuestRef, GuestRefMut}; -use crate::{GuestError, GuestType, GuestTypeCopy}; -use std::{ - fmt, - ops::{Deref, DerefMut}, -}; +use super::ptr::{GuestPtr, GuestRef}; +use crate::{GuestError, GuestType}; +use std::{fmt, ops::Deref}; pub struct GuestArray<'a, T> where @@ -28,31 +25,28 @@ where impl<'a, T> GuestArray<'a, T> where - T: GuestTypeCopy, + T: GuestType, { pub fn as_ref(&self) -> Result, GuestError> { - let mut ptr = self.ptr.clone(); + let mut refs = Vec::new(); + let mut next = self.ptr.elem(0)?; for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; + T::validate(&next)?; + let handle = { + let mut borrows = next.mem.borrows.borrow_mut(); + borrows + .borrow_immut(next.region) + .ok_or_else(|| GuestError::PtrBorrowed(next.region))? + }; + refs.push(GuestRef { + mem: next.mem, + region: next.region, + handle, + type_: next.type_, + }); + next = next.elem(1)?; } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_immut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_ = GuestRef { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRef { - ref_, - num_elems: self.num_elems, - }) + Ok(GuestArrayRef { refs }) } } @@ -60,8 +54,7 @@ pub struct GuestArrayRef<'a, T> where T: GuestType, { - ref_: GuestRef<'a, T>, - num_elems: u32, + refs: Vec>, } impl<'a, T> fmt::Debug for GuestArrayRef<'a, T> @@ -69,137 +62,18 @@ where T: GuestType + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}", - self.ref_, self.num_elems - ) + write!(f, "GuestArrayRef {{ refs: {:?} }}", self.refs) } } impl<'a, T> Deref for GuestArrayRef<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; - - fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -pub struct GuestArrayMut<'a, T> where T: GuestType, { - pub(super) ptr: GuestPtrMut<'a, T>, - pub(super) num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayMut<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayMut {{ ptr: {:?}, num_elems: {:?} }}", - self.ptr, self.num_elems - ) - } -} - -impl<'a, T> GuestArrayMut<'a, T> -where - T: GuestTypeCopy, -{ - pub fn as_ref(&self) -> Result, GuestError> { - let arr = GuestArray { - ptr: self.ptr.as_immut(), - num_elems: self.num_elems, - }; - arr.as_ref() - } - - pub fn as_ref_mut(&self) -> Result, GuestError> { - let mut ptr = self.ptr.as_immut(); - for _ in 0..self.num_elems { - ptr = ptr.elem(1)?; - T::validate(&ptr)?; - } - let region = self.ptr.region.extend((self.num_elems - 1) * T::size()); - let handle = { - let mut borrows = self.ptr.mem.borrows.borrow_mut(); - borrows - .borrow_mut(region) - .ok_or_else(|| GuestError::PtrBorrowed(region))? - }; - let ref_mut = GuestRefMut { - mem: self.ptr.mem, - region, - handle, - type_: self.ptr.type_, - }; - Ok(GuestArrayRefMut { - ref_mut, - num_elems: self.num_elems, - }) - } -} - -pub struct GuestArrayRefMut<'a, T> -where - T: GuestType, -{ - ref_mut: GuestRefMut<'a, T>, - num_elems: u32, -} - -impl<'a, T> fmt::Debug for GuestArrayRefMut<'a, T> -where - T: GuestType + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "GuestArrayRefMut {{ ref_mut: {:?}, num_elems: {:?} }}", - self.ref_mut, self.num_elems - ) - } -} - -impl<'a, T> Deref for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - type Target = [T]; + type Target = [GuestRef<'a, T>]; fn deref(&self) -> &Self::Target { - unsafe { - std::slice::from_raw_parts( - self.ref_mut.as_ptr().as_raw() as *const T, - self.num_elems as usize, - ) - } - } -} - -impl<'a, T> DerefMut for GuestArrayRefMut<'a, T> -where - T: GuestTypeCopy, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { - std::slice::from_raw_parts_mut( - self.ref_mut.as_ptr_mut().as_raw() as *mut T, - self.num_elems as usize, - ) - } + self.refs.as_slice() } } @@ -231,14 +105,10 @@ mod test { fn out_of_bounds() { let mut host_memory = HostMemory::new(); let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // try extracting an immutable array out of memory bounds + // try extracting an array out of memory bounds let ptr: GuestPtr = guest_memory.ptr(4092).expect("ptr to last i32 el"); let err = ptr.array(2).expect_err("out of bounds ptr error"); assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); - // try extracting an mutable array out of memory bounds - let ptr: GuestPtrMut = guest_memory.ptr_mut(4092).expect("ptr mut to last i32 el"); - let err = ptr.array_mut(2).expect_err("out of bounds ptr error"); - assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8))); } #[test] @@ -260,76 +130,77 @@ mod test { // extract as array let ptr: GuestPtr = guest_memory.ptr(0).expect("ptr to first el"); let arr = ptr.array(3).expect("convert ptr to array"); - let as_ref = arr.as_ref().expect("array borrowed immutably"); - assert_eq!(&*as_ref, &[1, 2, 3]); - // borrowing again should be fine - let as_ref2 = arr.as_ref().expect("array borrowed immutably again"); - assert_eq!(&*as_ref2, &*as_ref); - } - - #[test] - fn ptr_mut_to_array_mut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - // set elems of array to zero - { - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let mut el = ptr.as_ref_mut().expect("ref mut to first el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(4).expect("ptr mut to second el"); - let mut el = ptr.as_ref_mut().expect("ref mu to second el"); - *el = 0; - let ptr: GuestPtrMut = guest_memory.ptr_mut(8).expect("ptr mut to third el"); - let mut el = ptr.as_ref_mut().expect("ref mut to third el"); - *el = 0; - } - // extract as array and verify all is zero - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[0; 3]); - // populate the array and re-verify - for el in &mut *arr.as_ref_mut().expect("array borrowed mutably") { - *el = 10; - } - // re-validate - assert_eq!(&*arr.as_ref().expect("array borrowed immutably"), &[10; 3]); - } - - #[test] - #[should_panic( - expected = "array borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn borrow_mut_then_immut() { - let mut host_memory = HostMemory::new(); - let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // borrow immutably should fail - let _as_ref = arr + let as_ref = arr .as_ref() - .expect("array borrowed immutably while borrowed mutably"); + .expect("array borrowed immutably") + .iter() + .map(|x| **x) + .collect::>(); + assert_eq!(&as_ref, &[1, 2, 3]); + // borrowing again should be fine + let as_ref2 = arr + .as_ref() + .expect("array borrowed immutably again") + .iter() + .map(|x| **x) + .collect::>(); + assert_eq!(&as_ref2, &as_ref); } #[test] - #[should_panic( - expected = "array borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 12 })" - )] - fn borrow_mut_twice() { + fn ptr_to_ptr_array() { let mut host_memory = HostMemory::new(); let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let ptr: GuestPtrMut = guest_memory.ptr_mut(0).expect("ptr mut to first el"); - let arr = ptr.array_mut(3).expect("convert ptr mut to array mut"); - // borrow mutably - let _as_mut = arr - .as_ref_mut() - .expect("array borrowed mutably for the first time"); - // try borrowing mutably again - let _as_mut2 = arr - .as_ref_mut() - .expect("array borrowed mutably while borrowed mutably"); + { + let val_ptr: GuestPtrMut = + guest_memory.ptr_mut(0).expect("ptr mut to the first value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the first value"); + *val = 255; + let val_ptr: GuestPtrMut = guest_memory + .ptr_mut(4) + .expect("ptr mut to the second value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the second value"); + *val = 254; + let val_ptr: GuestPtrMut = + guest_memory.ptr_mut(8).expect("ptr mut to the third value"); + let mut val = val_ptr.as_ref_mut().expect("ref mut to the third value"); + *val = 253; + } + { + let ptr = guest_memory.ptr_mut(12).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(0) + .expect("ptr to the first value"), + ); + let ptr = guest_memory.ptr_mut(16).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(4) + .expect("ptr to the second value"), + ); + let ptr = guest_memory.ptr_mut(20).expect("ptr mut to first el"); + ptr.write_ptr_to_guest( + &guest_memory + .ptr::>(8) + .expect("ptr to the third value"), + ); + } + // extract as array + let ptr: GuestPtr> = guest_memory.ptr(12).expect("ptr to first el"); + let arr = ptr.array(3).expect("convert ptr to array"); + let as_ref = arr + .as_ref() + .expect("array borrowed immutably") + .iter() + .map(|x| { + *x.as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some value") + .as_ref() + .expect("deref ptr to some value") + }) + .collect::>(); + assert_eq!(&as_ref, &[255, 254, 253]); } } diff --git a/crates/runtime/src/memory/ptr.rs b/crates/runtime/src/memory/ptr.rs index e9f64ba715..da1fd3e4bc 100644 --- a/crates/runtime/src/memory/ptr.rs +++ b/crates/runtime/src/memory/ptr.rs @@ -1,7 +1,4 @@ -use super::{ - array::{GuestArray, GuestArrayMut}, - GuestMemory, -}; +use super::{array::GuestArray, GuestMemory}; use crate::{ borrow::BorrowHandle, GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestTypePtr, Region, @@ -47,7 +44,7 @@ impl<'a, T: GuestType> GuestPtr<'a, T> { } pub fn array(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); + let region = self.region.extend(num_elems); if self.mem.contains(region) { let ptr = GuestPtr { mem: self.mem, @@ -194,20 +191,6 @@ where ) -> Result, GuestError> { self.mem.ptr_mut(self.region.start + offset) } - - pub fn array_mut(&self, num_elems: u32) -> Result, GuestError> { - let region = self.region.extend((num_elems - 1) * T::size()); - if self.mem.contains(region) { - let ptr = GuestPtrMut { - mem: self.mem, - region: self.region, - type_: self.type_, - }; - Ok(GuestArrayMut { ptr, num_elems }) - } else { - Err(GuestError::PtrOutOfBounds(region)) - } - } } impl<'a, T> GuestPtrMut<'a, T> diff --git a/crates/runtime/src/region.rs b/crates/runtime/src/region.rs index fbbe752c85..e1e6084dc0 100644 --- a/crates/runtime/src/region.rs +++ b/crates/runtime/src/region.rs @@ -26,10 +26,11 @@ impl Region { } } - pub fn extend(&self, new_len: u32) -> Self { + pub fn extend(&self, times: u32) -> Self { + let len = self.len * times; Self { start: self.start, - len: self.len + new_len, + len, } } } diff --git a/tests/main.rs b/tests/main.rs index 89bfe21f52..a84be06951 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -92,6 +92,39 @@ impl foo::Foo for WasiCtx { .expect("dereferncing GuestPtr should succeed"); Ok(first as i64 + second as i64) } + + fn reduce_excuses( + &mut self, + excuses: &types::ConstExcuseArray, + ) -> Result { + let excuses = &*excuses + .as_ref() + .expect("dereferencing GuestArray should succeed"); + let last = excuses + .last() + .expect("input array is non-empty") + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + Ok(*last.as_ref().expect("dereferencing ptr should succeed")) + } + + fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + let excuses = &*excuses + .as_ref() + .expect("dereferencing GuestArray should succeed"); + for excuse in excuses { + let ptr_to_ptr = excuse + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + let mut ptr = ptr_to_ptr + .as_ref_mut() + .expect("dereferencing mut ptr should succeed"); + *ptr = types::Excuse::Sleeping; + } + Ok(()) + } } // Errno is used as a first return value in the functions above, therefore // it must implement GuestErrorType with type Context = WasiCtx. @@ -125,10 +158,14 @@ impl HostMemory { } pub fn mem_area_strat(align: u32) -> BoxedStrategy { prop::num::u32::ANY - .prop_map(move |p| { + .prop_filter_map("needs to fit in memory", move |p| { let p_aligned = p - (p % align); // Align according to argument let ptr = p_aligned % 4096; // Put inside memory - MemArea { ptr, len: align } + if ptr + align < 4096 { + Some(MemArea { ptr, len: align }) + } else { + None + } }) .boxed() } @@ -148,12 +185,12 @@ fn overlapping(a: &MemArea, b: &MemArea) -> bool { // a_range is all elems in A let a_range = std::ops::Range { start: a.ptr, - end: a.ptr + a.len - 1, + end: a.ptr + a.len, // std::ops::Range is open from the right }; // b_range is all elems in B let b_range = std::ops::Range { start: b.ptr, - end: b.ptr + b.len - 1, + end: b.ptr + b.len, }; // No element in B is contained in A: for b_elem in b_range.clone() { @@ -542,3 +579,207 @@ proptest! { e.test() } } + +#[derive(Debug)] +struct ReduceExcusesExcercise { + excuse_values: Vec, + excuse_ptr_locs: Vec, + array_ptr_loc: MemArea, + array_len_loc: MemArea, + return_ptr_loc: MemArea, +} + +impl ReduceExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + proptest::collection::vec(excuse_strat(), len_usize..=len_usize), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + }) + .prop_map( + |(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| { + Self { + excuse_values, + excuse_ptr_locs, + array_ptr_loc, + array_len_loc, + return_ptr_loc, + } + }, + ) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc]; + all.extend(e.excuse_ptr_locs.iter()); + non_overlapping_set(&all) + }) + .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); + + // Populate memory with pointers to generated Excuse values + for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { + *guest_memory + .ptr_mut(ptr.ptr) + .expect("ptr mut to Excuse value") + .as_ref_mut() + .expect("deref ptr mut to Excuse value") = excuse; + } + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32; + + // Populate the array with pointers to generated Excuse values + { + let mut next: GuestPtrMut<'_, GuestPtr> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr to array mut"); + for ptr in &self.excuse_ptr_locs { + next.write_ptr_to_guest( + &guest_memory + .ptr::(ptr.ptr) + .expect("ptr to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = foo::reduce_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + self.return_ptr_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "reduce excuses errno"); + + let expected = *self + .excuse_values + .last() + .expect("generated vec of excuses should be non-empty"); + let given: types::Excuse = *guest_memory + .ptr(self.return_ptr_loc.ptr) + .expect("ptr to returned value") + .as_ref() + .expect("deref ptr to returned value"); + assert_eq!(expected, given, "reduce excuses return val"); + } +} +proptest! { + #[test] + fn reduce_excuses(e in ReduceExcusesExcercise::strat()) { + e.test() + } +} + +#[derive(Debug)] +struct PopulateExcusesExcercise { + array_ptr_loc: MemArea, + array_len_loc: MemArea, + elements: Vec, +} + +impl PopulateExcusesExcercise { + pub fn strat() -> BoxedStrategy { + (1..256u32) + .prop_flat_map(|len| { + let len_usize = len as usize; + ( + HostMemory::mem_area_strat(4 * len), + HostMemory::mem_area_strat(4), + proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize), + ) + }) + .prop_map(|(array_ptr_loc, array_len_loc, elements)| Self { + array_ptr_loc, + array_len_loc, + elements, + }) + .prop_filter("non-overlapping pointers", |e| { + let mut all = vec![&e.array_ptr_loc, &e.array_len_loc]; + all.extend(e.elements.iter()); + non_overlapping_set(&all) + }) + .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); + + // Populate array length info + *guest_memory + .ptr_mut(self.array_len_loc.ptr) + .expect("ptr mut to array len") + .as_ref_mut() + .expect("deref ptr mut to array len") = self.elements.len() as u32; + + // Populate array with valid pointers to Excuse type in memory + { + let mut next: GuestPtrMut<'_, GuestPtrMut> = guest_memory + .ptr_mut(self.array_ptr_loc.ptr) + .expect("ptr mut to the first element of array"); + for ptr in &self.elements { + next.write_ptr_to_guest( + &guest_memory + .ptr_mut::(ptr.ptr) + .expect("ptr mut to Excuse value"), + ); + next = next.elem(1).expect("increment ptr by 1"); + } + } + + let res = foo::populate_excuses( + &mut ctx, + &mut guest_memory, + self.array_ptr_loc.ptr as i32, + self.array_len_loc.ptr as i32, + ); + assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno"); + + let arr = { + let ptr: GuestPtr<'_, GuestPtr<'_, types::Excuse>> = guest_memory + .ptr(self.array_ptr_loc.ptr) + .expect("ptr to the first element of array"); + &*ptr + .array(self.elements.len() as u32) + .expect("as array") + .as_ref() + .expect("deref to &[]") + }; + for el in arr { + let ptr_to_ptr = el + .as_ptr() + .read_ptr_from_guest() + .expect("valid ptr to some Excuse value"); + assert_eq!( + *ptr_to_ptr + .as_ref() + .expect("dereferencing ptr to some Excuse value"), + types::Excuse::Sleeping, + "element should equal Excuse::Sleeping" + ); + } + } +} +proptest! { + #[test] + fn populate_excuses(e in PopulateExcusesExcercise::strat()) { + e.test() + } +} diff --git a/tests/test.witx b/tests/test.witx index 40e2aab7ed..e5b0c6bbf1 100644 --- a/tests/test.witx +++ b/tests/test.witx @@ -25,6 +25,9 @@ (typename $named_ptr (@witx pointer f32)) (typename $named_ptr_to_ptr (@witx pointer (@witx pointer f64))) +(typename $const_excuse_array (array (@witx const_pointer $excuse))) +(typename $excuse_array (array (@witx pointer $excuse))) + (module $foo (@interface func (export "bar") (param $an_int u32) @@ -48,4 +51,13 @@ (param $an_pair $pair_int_ptrs) (result $error $errno) (result $doubled s64)) + (@interface func (export "reduce_excuses") + (param $excuses $const_excuse_array) + (result $error $errno) + (result $reduced $excuse) + ) + (@interface func (export "populate_excuses") + (param $excuses $excuse_array) + (result $error $errno) + ) )