Add array generation to wiggle-generate crate (#9)
* Add array generation to wiggle-generate crate This commit: * adds array generation to `wiggle-generate` crate which implies that we can now generate arrays from `witx` files * introduces two test interface functions `foo::reduce_excuses` and `foo::populate_excuses`, and adds matching prop-tests * adds an out-of-boundary check to `HostMemory::mem_area_strat` since now, given we're generating arrays for testing with an arbitrary but bounded number of elements, it is possible to violate the boundary * refactors `Region::extend` to a new signature `extend(times: u32)` which multiplies the current pointer `len` by `times` * fixes bug in `GuestArray::as_ref` and `GuestArrayMut::as_ref_mut` methods where we were not validating the first element (we always started the validation from the second element) * Fix generation of arrays in witx This commit fixes how `arrays` are auto-generated from `witx` files. In particular, the changes include: * Since by design every `array` in `witx` represents an immutable slab of memory, we will only ever operate on `GuestArray` in which case I've gone ahead and removed `GuestArrayMut` so as to unclutter the code somewhat. If we find ourselves in need for it in the future, I reckon it will be better to write it from scratch (since the codebase will inevitably evolve by then considerably) rather than maintaining an unused implementation. * I've rewritten `GuestArrayRef<T>` to be a wrapper for `Vec<GuestRef<T>>`. Also, `GuestArray::as_ref` now borrows each underlying "element" of the array one-by-one rather than borrowing the entire chunk of memory at once. This change is motivated by the inability to coerce type parameter `T` in `GuestArray<T>` in more complicated cases such as arrays of guest pointers `GuestPtr<T>` to `*const T` for reuse in `std::slice::from_raw_parts` call. (In general, the size of Wasm32 pointer is 4 bytes, while ``` std::mem::size_of::<T>() == std::mem::size_of::<GuestPtr<S>>() == 16 ``` which is problematic; i.e., I can't see how I could properly extract guest pointers from slices of 4 bytes and at the same time not allocate.) * I've augmented fuzz tests by (re-)defining two `array` types: ``` (typename $const_excuse_array (array (@witx const_pointer $excuse))) (typename $excuse_array (array (@witx pointer $excuse))) ``` This should hopefully emulate and test the `iovec` and `ciovec` arrays present in WASI spec.
This commit is contained in:
@@ -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::<u32>(#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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<GuestArrayRef<'a, T>, 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<GuestRef<'a, T>>,
|
||||
}
|
||||
|
||||
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<GuestArrayRef<'a, T>, GuestError> {
|
||||
let arr = GuestArray {
|
||||
ptr: self.ptr.as_immut(),
|
||||
num_elems: self.num_elems,
|
||||
};
|
||||
arr.as_ref()
|
||||
}
|
||||
|
||||
pub fn as_ref_mut(&self) -> Result<GuestArrayRefMut<'a, T>, 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<i32> = 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<i32> = 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<i32> = 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<i32> = 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<i32> = 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<i32> = 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<i32> = 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<i32> = 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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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<i32> = 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<u8> =
|
||||
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<u8> = 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<u8> =
|
||||
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::<GuestPtr<u8>>(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::<GuestPtr<u8>>(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::<GuestPtr<u8>>(8)
|
||||
.expect("ptr to the third value"),
|
||||
);
|
||||
}
|
||||
// extract as array
|
||||
let ptr: GuestPtr<GuestPtr<u8>> = 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::<Vec<_>>();
|
||||
assert_eq!(&as_ref, &[255, 254, 253]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GuestArray<'a, T>, 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<GuestPtrMut<'a, CastTo>, GuestError> {
|
||||
self.mem.ptr_mut(self.region.start + offset)
|
||||
}
|
||||
|
||||
pub fn array_mut(&self, num_elems: u32) -> Result<GuestArrayMut<'a, T>, 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>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
249
tests/main.rs
249
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<types::Excuse, types::Errno> {
|
||||
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<MemArea> {
|
||||
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<types::Excuse>,
|
||||
excuse_ptr_locs: Vec<MemArea>,
|
||||
array_ptr_loc: MemArea,
|
||||
array_len_loc: MemArea,
|
||||
return_ptr_loc: MemArea,
|
||||
}
|
||||
|
||||
impl ReduceExcusesExcercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(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<types::Excuse>> = 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::<types::Excuse>(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<MemArea>,
|
||||
}
|
||||
|
||||
impl PopulateExcusesExcercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(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<types::Excuse>> = 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::<types::Excuse>(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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user