Add 'crates/wiggle/' from commit 'cd484e49932d8dd8f1bd1a002e0717ad8bff07fb'
git-subtree-dir: crates/wiggle git-subtree-mainline:2ead747f48git-subtree-split:cd484e4993
This commit is contained in:
210
crates/wiggle/tests/arrays.rs
Normal file
210
crates/wiggle/tests/arrays.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/arrays.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> arrays::Arrays for WasiCtx<'a> {
|
||||
fn reduce_excuses(
|
||||
&self,
|
||||
excuses: &types::ConstExcuseArray,
|
||||
) -> Result<types::Excuse, types::Errno> {
|
||||
let last = &excuses
|
||||
.iter()
|
||||
.last()
|
||||
.expect("input array is non-empty")
|
||||
.expect("valid ptr to ptr")
|
||||
.read()
|
||||
.expect("valid ptr to some Excuse value");
|
||||
Ok(last.read().expect("dereferencing ptr should succeed"))
|
||||
}
|
||||
|
||||
fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
|
||||
for excuse in excuses.iter() {
|
||||
let ptr_to_excuse = excuse
|
||||
.expect("valid ptr to ptr")
|
||||
.read()
|
||||
.expect("valid ptr to some Excuse value");
|
||||
ptr_to_excuse
|
||||
.write(types::Excuse::Sleeping)
|
||||
.expect("dereferencing mut ptr should succeed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReduceExcusesExcercise {
|
||||
excuse_values: Vec<types::Excuse>,
|
||||
excuse_ptr_locs: Vec<MemArea>,
|
||||
array_ptr_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),
|
||||
)
|
||||
})
|
||||
.prop_map(
|
||||
|(excuse_values, excuse_ptr_locs, array_ptr_loc, return_ptr_loc)| Self {
|
||||
excuse_values,
|
||||
excuse_ptr_locs,
|
||||
array_ptr_loc,
|
||||
return_ptr_loc,
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
let mut all = vec![e.array_ptr_loc, e.return_ptr_loc];
|
||||
all.extend(e.excuse_ptr_locs.iter());
|
||||
MemArea::non_overlapping_set(all)
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let mut ctx = WasiCtx::new();
|
||||
let mut host_memory = HostMemory::new();
|
||||
|
||||
// Populate memory with pointers to generated Excuse values
|
||||
for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) {
|
||||
host_memory
|
||||
.ptr(ptr.ptr)
|
||||
.write(excuse)
|
||||
.expect("deref ptr mut to Excuse value");
|
||||
}
|
||||
|
||||
// Populate the array with pointers to generated Excuse values
|
||||
{
|
||||
let array: GuestPtr<'_, [GuestPtr<types::Excuse>]> =
|
||||
host_memory.ptr((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32));
|
||||
for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) {
|
||||
let slot = slot.expect("array should be in bounds");
|
||||
slot.write(host_memory.ptr(ptr.ptr))
|
||||
.expect("should succeed in writing array");
|
||||
}
|
||||
}
|
||||
|
||||
let res = arrays::reduce_excuses(
|
||||
&mut ctx,
|
||||
&mut host_memory,
|
||||
self.array_ptr_loc.ptr as i32,
|
||||
self.excuse_ptr_locs.len() 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 = host_memory
|
||||
.ptr(self.return_ptr_loc.ptr)
|
||||
.read()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
||||
fn excuse_strat() -> impl Strategy<Value = types::Excuse> {
|
||||
prop_oneof![
|
||||
Just(types::Excuse::DogAte),
|
||||
Just(types::Excuse::Traffic),
|
||||
Just(types::Excuse::Sleeping),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PopulateExcusesExcercise {
|
||||
array_ptr_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),
|
||||
proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
|
||||
)
|
||||
})
|
||||
.prop_map(|(array_ptr_loc, elements)| Self {
|
||||
array_ptr_loc,
|
||||
elements,
|
||||
})
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
let mut all = vec![e.array_ptr_loc];
|
||||
all.extend(e.elements.iter());
|
||||
MemArea::non_overlapping_set(all)
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate array with valid pointers to Excuse type in memory
|
||||
let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>((
|
||||
self.array_ptr_loc.ptr,
|
||||
self.elements.len() as u32,
|
||||
));
|
||||
for (ptr, val) in ptr.iter().zip(&self.elements) {
|
||||
ptr.expect("should be valid pointer")
|
||||
.write(host_memory.ptr(val.ptr))
|
||||
.expect("failed to write value");
|
||||
}
|
||||
|
||||
let res = arrays::populate_excuses(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.array_ptr_loc.ptr as i32,
|
||||
self.elements.len() as i32,
|
||||
);
|
||||
assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno");
|
||||
|
||||
let arr: GuestPtr<'_, [GuestPtr<'_, types::Excuse>]> =
|
||||
host_memory.ptr((self.array_ptr_loc.ptr, self.elements.len() as u32));
|
||||
for el in arr.iter() {
|
||||
let ptr_to_ptr = el
|
||||
.expect("valid ptr to ptr")
|
||||
.read()
|
||||
.expect("valid ptr to some Excuse value");
|
||||
assert_eq!(
|
||||
ptr_to_ptr
|
||||
.read()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
17
crates/wiggle/tests/arrays.witx
Normal file
17
crates/wiggle/tests/arrays.witx
Normal file
@@ -0,0 +1,17 @@
|
||||
(use "errno.witx")
|
||||
(use "excuse.witx")
|
||||
|
||||
(typename $const_excuse_array (array (@witx const_pointer $excuse)))
|
||||
(typename $excuse_array (array (@witx pointer $excuse)))
|
||||
|
||||
(module $arrays
|
||||
(@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)
|
||||
)
|
||||
)
|
||||
91
crates/wiggle/tests/atoms.rs
Normal file
91
crates/wiggle/tests/atoms.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/atoms.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> atoms::Atoms for WasiCtx<'a> {
|
||||
fn int_float_args(&self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
||||
Ok(())
|
||||
}
|
||||
fn double_int_return_float(&self, an_int: u32) -> Result<types::AliasToFloat, types::Errno> {
|
||||
Ok((an_int as f32) * 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
// There's nothing meaningful to test here - this just demonstrates the test machinery
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IntFloatExercise {
|
||||
pub an_int: u32,
|
||||
pub an_float: f32,
|
||||
}
|
||||
|
||||
impl IntFloatExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = atoms::int_float_args(&ctx, &host_memory, self.an_int as i32, self.an_float);
|
||||
|
||||
assert_eq!(e, types::Errno::Ok.into(), "int_float_args error");
|
||||
}
|
||||
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(prop::num::u32::ANY, prop::num::f32::ANY)
|
||||
.prop_map(|(an_int, an_float)| IntFloatExercise { an_int, an_float })
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn int_float_exercise(e in IntFloatExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct DoubleIntExercise {
|
||||
pub input: u32,
|
||||
pub return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl DoubleIntExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = atoms::double_int_return_float(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
let return_val = host_memory
|
||||
.ptr::<types::AliasToFloat>(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("failed to read return");
|
||||
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
||||
assert_eq!(return_val, (self.input as f32) * 2.0, "return val");
|
||||
}
|
||||
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(prop::num::u32::ANY, HostMemory::mem_area_strat(4))
|
||||
.prop_map(|(input, return_loc)| DoubleIntExercise { input, return_loc })
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn double_int_return_float(e in DoubleIntExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
14
crates/wiggle/tests/atoms.witx
Normal file
14
crates/wiggle/tests/atoms.witx
Normal file
@@ -0,0 +1,14 @@
|
||||
(use "errno.witx")
|
||||
|
||||
(typename $alias_to_float f32)
|
||||
|
||||
(module $atoms
|
||||
(@interface func (export "int_float_args")
|
||||
(param $an_int u32)
|
||||
(param $an_float f32)
|
||||
(result $error $errno))
|
||||
(@interface func (export "double_int_return_float")
|
||||
(param $an_int u32)
|
||||
(result $error $errno)
|
||||
(result $doubled_it $alias_to_float))
|
||||
)
|
||||
13
crates/wiggle/tests/errno.witx
Normal file
13
crates/wiggle/tests/errno.witx
Normal file
@@ -0,0 +1,13 @@
|
||||
(typename $errno
|
||||
(enum u32
|
||||
;;; Success
|
||||
$ok
|
||||
;;; Invalid argument
|
||||
$invalid_arg
|
||||
;;; I really don't want to
|
||||
$dont_want_to
|
||||
;;; I am physically unable to
|
||||
$physically_unable
|
||||
;;; Well, that's a picket line alright!
|
||||
$picket_line))
|
||||
|
||||
6
crates/wiggle/tests/excuse.witx
Normal file
6
crates/wiggle/tests/excuse.witx
Normal file
@@ -0,0 +1,6 @@
|
||||
(typename $excuse
|
||||
(enum u8
|
||||
$dog_ate
|
||||
$traffic
|
||||
$sleeping))
|
||||
|
||||
101
crates/wiggle/tests/flags.rs
Normal file
101
crates/wiggle/tests/flags.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use proptest::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/flags.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> flags::Flags for WasiCtx<'a> {
|
||||
fn configure_car(
|
||||
&self,
|
||||
old_config: types::CarConfig,
|
||||
other_config_ptr: GuestPtr<types::CarConfig>,
|
||||
) -> Result<types::CarConfig, types::Errno> {
|
||||
let other_config = other_config_ptr.read().map_err(|e| {
|
||||
eprintln!("old_config_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
Ok(old_config ^ other_config)
|
||||
}
|
||||
}
|
||||
|
||||
fn car_config_strat() -> impl Strategy<Value = types::CarConfig> {
|
||||
(1u8..=types::CarConfig::ALL_FLAGS.into())
|
||||
.prop_map(|v| {
|
||||
types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag")
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ConfigureCarExercise {
|
||||
old_config: types::CarConfig,
|
||||
other_config: types::CarConfig,
|
||||
other_config_by_ptr: MemArea,
|
||||
return_ptr_loc: MemArea,
|
||||
}
|
||||
|
||||
impl ConfigureCarExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
car_config_strat(),
|
||||
car_config_strat(),
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(4),
|
||||
)
|
||||
.prop_map(
|
||||
|(old_config, other_config, other_config_by_ptr, return_ptr_loc)| Self {
|
||||
old_config,
|
||||
other_config,
|
||||
other_config_by_ptr,
|
||||
return_ptr_loc,
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping ptrs", |e| {
|
||||
MemArea::non_overlapping_set(&[e.other_config_by_ptr, e.return_ptr_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate input ptr
|
||||
host_memory
|
||||
.ptr(self.other_config_by_ptr.ptr)
|
||||
.write(self.other_config)
|
||||
.expect("deref ptr mut to CarConfig");
|
||||
|
||||
let res = flags::configure_car(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.old_config.into(),
|
||||
self.other_config_by_ptr.ptr as i32,
|
||||
self.return_ptr_loc.ptr as i32,
|
||||
);
|
||||
assert_eq!(res, types::Errno::Ok.into(), "configure car errno");
|
||||
|
||||
let res_config = host_memory
|
||||
.ptr::<types::CarConfig>(self.return_ptr_loc.ptr)
|
||||
.read()
|
||||
.expect("deref to CarConfig value");
|
||||
|
||||
assert_eq!(
|
||||
self.old_config ^ self.other_config,
|
||||
res_config,
|
||||
"returned CarConfig should be an XOR of inputs"
|
||||
);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn configure_car(e in ConfigureCarExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
16
crates/wiggle/tests/flags.witx
Normal file
16
crates/wiggle/tests/flags.witx
Normal file
@@ -0,0 +1,16 @@
|
||||
(use "errno.witx")
|
||||
|
||||
(typename $car_config
|
||||
(flags u8
|
||||
$automatic
|
||||
$awd
|
||||
$suv))
|
||||
|
||||
(module $flags
|
||||
(@interface func (export "configure_car")
|
||||
(param $old_config $car_config)
|
||||
(param $old_config_by_ptr (@witx const_pointer $car_config))
|
||||
(result $error $errno)
|
||||
(result $new_config $car_config)
|
||||
)
|
||||
)
|
||||
74
crates/wiggle/tests/handles.rs
Normal file
74
crates/wiggle/tests/handles.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestType};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
const FD_VAL: u32 = 123;
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/handles.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> handle_examples::HandleExamples for WasiCtx<'a> {
|
||||
fn fd_create(&self) -> Result<types::Fd, types::Errno> {
|
||||
Ok(types::Fd::from(FD_VAL))
|
||||
}
|
||||
fn fd_consume(&self, fd: types::Fd) -> Result<(), types::Errno> {
|
||||
println!("FD_CONSUME {}", fd);
|
||||
if fd == types::Fd::from(FD_VAL) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(types::Errno::InvalidArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HandleExercise {
|
||||
pub return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl HandleExercise {
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let e = handle_examples::fd_create(&ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
|
||||
assert_eq!(e, types::Errno::Ok.into(), "fd_create error");
|
||||
|
||||
let h_got: u32 = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref_mut");
|
||||
|
||||
assert_eq!(h_got, 123, "fd_create return val");
|
||||
|
||||
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32);
|
||||
|
||||
assert_eq!(e, types::Errno::Ok.into(), "fd_consume error");
|
||||
|
||||
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32 + 1);
|
||||
|
||||
assert_eq!(
|
||||
e,
|
||||
types::Errno::InvalidArg.into(),
|
||||
"fd_consume invalid error"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(HostMemory::mem_area_strat(types::Fd::guest_size()))
|
||||
.prop_map(|return_loc| HandleExercise { return_loc })
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn handle_exercise(e in HandleExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
12
crates/wiggle/tests/handles.witx
Normal file
12
crates/wiggle/tests/handles.witx
Normal file
@@ -0,0 +1,12 @@
|
||||
(use "errno.witx")
|
||||
|
||||
(typename $fd (handle))
|
||||
|
||||
(module $handle_examples
|
||||
(@interface func (export "fd_create")
|
||||
(result $error $errno)
|
||||
(result $fd $fd))
|
||||
(@interface func (export "fd_consume")
|
||||
(param $fd $fd)
|
||||
(result $error $errno))
|
||||
)
|
||||
79
crates/wiggle/tests/ints.rs
Normal file
79
crates/wiggle/tests/ints.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use proptest::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use wiggle_runtime::{GuestError, GuestMemory};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/ints.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> ints::Ints for WasiCtx<'a> {
|
||||
fn cookie_cutter(&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)
|
||||
}
|
||||
}
|
||||
|
||||
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 ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let res = ints::cookie_cutter(
|
||||
&ctx,
|
||||
&host_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 = host_memory
|
||||
.ptr::<types::Bool>(self.return_ptr_loc.ptr)
|
||||
.read()
|
||||
.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
crates/wiggle/tests/ints.witx
Normal file
18
crates/wiggle/tests/ints.witx
Normal file
@@ -0,0 +1,18 @@
|
||||
(use "errno.witx")
|
||||
|
||||
(typename $cookie
|
||||
(int u64
|
||||
(const $start 0)))
|
||||
|
||||
(typename $bool
|
||||
(enum u8
|
||||
$false
|
||||
$true))
|
||||
|
||||
(module $ints
|
||||
(@interface func (export "cookie_cutter")
|
||||
(param $init_cookie $cookie)
|
||||
(result $error $errno)
|
||||
(result $is_start $bool)
|
||||
)
|
||||
)
|
||||
191
crates/wiggle/tests/pointers.rs
Normal file
191
crates/wiggle/tests/pointers.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/pointers.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> pointers::Pointers for WasiCtx<'a> {
|
||||
fn pointers_and_enums<'b>(
|
||||
&self,
|
||||
input1: types::Excuse,
|
||||
input2_ptr: GuestPtr<'b, types::Excuse>,
|
||||
input3_ptr: GuestPtr<'b, types::Excuse>,
|
||||
input4_ptr_ptr: GuestPtr<'b, GuestPtr<'b, types::Excuse>>,
|
||||
) -> Result<(), types::Errno> {
|
||||
println!("BAZ input1 {:?}", input1);
|
||||
let input2: types::Excuse = input2_ptr.read().map_err(|e| {
|
||||
eprintln!("input2_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
println!("input2 {:?}", input2);
|
||||
|
||||
// Read enum value from immutable ptr:
|
||||
let input3 = input3_ptr.read().map_err(|e| {
|
||||
eprintln!("input3_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
println!("input3 {:?}", input3);
|
||||
|
||||
// Write enum to mutable ptr:
|
||||
input2_ptr.write(input3).map_err(|e| {
|
||||
eprintln!("input2_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
println!("wrote to input2_ref {:?}", input3);
|
||||
|
||||
// Read ptr value from mutable ptr:
|
||||
let input4_ptr: GuestPtr<types::Excuse> = input4_ptr_ptr.read().map_err(|e| {
|
||||
eprintln!("input4_ptr_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
|
||||
// Read enum value from that ptr:
|
||||
let input4: types::Excuse = input4_ptr.read().map_err(|e| {
|
||||
eprintln!("input4_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
println!("input4 {:?}", input4);
|
||||
|
||||
// Write ptr value to mutable ptr:
|
||||
input4_ptr_ptr.write(input2_ptr).map_err(|e| {
|
||||
eprintln!("input4_ptr_ptr error: {}", e);
|
||||
types::Errno::InvalidArg
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn excuse_strat() -> impl Strategy<Value = types::Excuse> {
|
||||
prop_oneof![
|
||||
Just(types::Excuse::DogAte),
|
||||
Just(types::Excuse::Traffic),
|
||||
Just(types::Excuse::Sleeping),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PointersAndEnumsExercise {
|
||||
pub input1: types::Excuse,
|
||||
pub input2: types::Excuse,
|
||||
pub input2_loc: MemArea,
|
||||
pub input3: types::Excuse,
|
||||
pub input3_loc: MemArea,
|
||||
pub input4: types::Excuse,
|
||||
pub input4_loc: MemArea,
|
||||
pub input4_ptr_loc: MemArea,
|
||||
}
|
||||
|
||||
impl PointersAndEnumsExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
excuse_strat(),
|
||||
excuse_strat(),
|
||||
HostMemory::mem_area_strat(4),
|
||||
excuse_strat(),
|
||||
HostMemory::mem_area_strat(4),
|
||||
excuse_strat(),
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(4),
|
||||
)
|
||||
.prop_map(
|
||||
|(
|
||||
input1,
|
||||
input2,
|
||||
input2_loc,
|
||||
input3,
|
||||
input3_loc,
|
||||
input4,
|
||||
input4_loc,
|
||||
input4_ptr_loc,
|
||||
)| PointersAndEnumsExercise {
|
||||
input1,
|
||||
input2,
|
||||
input2_loc,
|
||||
input3,
|
||||
input3_loc,
|
||||
input4,
|
||||
input4_loc,
|
||||
input4_ptr_loc,
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[
|
||||
e.input2_loc,
|
||||
e.input3_loc,
|
||||
e.input4_loc,
|
||||
e.input4_ptr_loc,
|
||||
])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
.ptr(self.input2_loc.ptr)
|
||||
.write(self.input2)
|
||||
.expect("input2 ref_mut");
|
||||
|
||||
host_memory
|
||||
.ptr(self.input3_loc.ptr)
|
||||
.write(self.input3)
|
||||
.expect("input3 ref_mut");
|
||||
|
||||
host_memory
|
||||
.ptr(self.input4_loc.ptr)
|
||||
.write(self.input4)
|
||||
.expect("input4 ref_mut");
|
||||
|
||||
host_memory
|
||||
.ptr(self.input4_ptr_loc.ptr)
|
||||
.write(self.input4_loc.ptr)
|
||||
.expect("input4 ptr ref_mut");
|
||||
|
||||
let e = pointers::pointers_and_enums(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input1.into(),
|
||||
self.input2_loc.ptr as i32,
|
||||
self.input3_loc.ptr as i32,
|
||||
self.input4_ptr_loc.ptr as i32,
|
||||
);
|
||||
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
||||
|
||||
// Implementation of pointers_and_enums writes input3 to the input2_loc:
|
||||
let written_to_input2_loc: i32 = host_memory
|
||||
.ptr(self.input2_loc.ptr)
|
||||
.read()
|
||||
.expect("input2 ref");
|
||||
|
||||
assert_eq!(
|
||||
written_to_input2_loc,
|
||||
self.input3.into(),
|
||||
"pointers_and_enums written to input2"
|
||||
);
|
||||
|
||||
// Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc:
|
||||
let written_to_input4_ptr: u32 = host_memory
|
||||
.ptr(self.input4_ptr_loc.ptr)
|
||||
.read()
|
||||
.expect("input4_ptr_loc ref");
|
||||
|
||||
assert_eq!(
|
||||
written_to_input4_ptr, self.input2_loc.ptr,
|
||||
"pointers_and_enums written to input4_ptr"
|
||||
);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn pointers_and_enums(e in PointersAndEnumsExercise::strat()) {
|
||||
e.test();
|
||||
}
|
||||
}
|
||||
11
crates/wiggle/tests/pointers.witx
Normal file
11
crates/wiggle/tests/pointers.witx
Normal file
@@ -0,0 +1,11 @@
|
||||
(use "errno.witx")
|
||||
(use "excuse.witx")
|
||||
|
||||
(module $pointers
|
||||
(@interface func (export "pointers_and_enums")
|
||||
(param $an_excuse $excuse)
|
||||
(param $an_excuse_by_reference (@witx pointer $excuse))
|
||||
(param $a_lamer_excuse (@witx const_pointer $excuse))
|
||||
(param $two_layers_of_excuses (@witx pointer (@witx const_pointer $excuse)))
|
||||
(result $error $errno))
|
||||
)
|
||||
223
crates/wiggle/tests/strings.rs
Normal file
223
crates/wiggle/tests/strings.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestBorrows, GuestError, GuestMemory, GuestPtr};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, MemAreas, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/strings.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> strings::Strings for WasiCtx<'a> {
|
||||
fn hello_string(&self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> {
|
||||
let mut bc = GuestBorrows::new();
|
||||
let s = a_string.as_raw(&mut bc).expect("should be valid string");
|
||||
unsafe {
|
||||
println!("a_string='{}'", &*s);
|
||||
Ok((*s).len() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
fn multi_string(
|
||||
&self,
|
||||
a: &GuestPtr<str>,
|
||||
b: &GuestPtr<str>,
|
||||
c: &GuestPtr<str>,
|
||||
) -> Result<u32, types::Errno> {
|
||||
let mut bc = GuestBorrows::new();
|
||||
let sa = a.as_raw(&mut bc).expect("A should be valid string");
|
||||
let sb = b.as_raw(&mut bc).expect("B should be valid string");
|
||||
let sc = c.as_raw(&mut bc).expect("C should be valid string");
|
||||
unsafe {
|
||||
let total_len = (&*sa).len() + (&*sb).len() + (&*sc).len();
|
||||
println!(
|
||||
"len={}, a='{}', b='{}', c='{}'",
|
||||
total_len, &*sa, &*sb, &*sc
|
||||
);
|
||||
Ok(total_len as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_string_strategy() -> impl Strategy<Value = String> {
|
||||
"\\p{Greek}{1,256}"
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HelloStringExercise {
|
||||
test_word: String,
|
||||
string_ptr_loc: MemArea,
|
||||
return_ptr_loc: MemArea,
|
||||
}
|
||||
|
||||
impl HelloStringExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(test_string_strategy(),)
|
||||
.prop_flat_map(|(test_word,)| {
|
||||
(
|
||||
Just(test_word.clone()),
|
||||
HostMemory::mem_area_strat(test_word.len() as u32),
|
||||
HostMemory::mem_area_strat(4),
|
||||
)
|
||||
})
|
||||
.prop_map(|(test_word, string_ptr_loc, return_ptr_loc)| Self {
|
||||
test_word,
|
||||
string_ptr_loc,
|
||||
return_ptr_loc,
|
||||
})
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.string_ptr_loc, e.return_ptr_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
// Populate string in guest's memory
|
||||
let ptr = host_memory.ptr::<str>((self.string_ptr_loc.ptr, self.test_word.len() as u32));
|
||||
for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) {
|
||||
slot.expect("should be valid pointer")
|
||||
.write(byte)
|
||||
.expect("failed to write");
|
||||
}
|
||||
|
||||
let res = strings::hello_string(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.string_ptr_loc.ptr as i32,
|
||||
self.test_word.len() as i32,
|
||||
self.return_ptr_loc.ptr as i32,
|
||||
);
|
||||
assert_eq!(res, types::Errno::Ok.into(), "hello string errno");
|
||||
|
||||
let given = host_memory
|
||||
.ptr::<u32>(self.return_ptr_loc.ptr)
|
||||
.read()
|
||||
.expect("deref ptr to return value");
|
||||
assert_eq!(self.test_word.len() as u32, given);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn hello_string(e in HelloStringExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MultiStringExercise {
|
||||
a: String,
|
||||
b: String,
|
||||
c: String,
|
||||
sa_ptr_loc: MemArea,
|
||||
sb_ptr_loc: MemArea,
|
||||
sc_ptr_loc: MemArea,
|
||||
return_ptr_loc: MemArea,
|
||||
}
|
||||
|
||||
impl MultiStringExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
test_string_strategy(),
|
||||
test_string_strategy(),
|
||||
test_string_strategy(),
|
||||
HostMemory::mem_area_strat(4),
|
||||
)
|
||||
.prop_flat_map(|(a, b, c, return_ptr_loc)| {
|
||||
(
|
||||
Just(a.clone()),
|
||||
Just(b.clone()),
|
||||
Just(c.clone()),
|
||||
HostMemory::byte_slice_strat(a.len() as u32, &MemAreas::from([return_ptr_loc])),
|
||||
Just(return_ptr_loc),
|
||||
)
|
||||
})
|
||||
.prop_flat_map(|(a, b, c, sa_ptr_loc, return_ptr_loc)| {
|
||||
(
|
||||
Just(a.clone()),
|
||||
Just(b.clone()),
|
||||
Just(c.clone()),
|
||||
Just(sa_ptr_loc),
|
||||
HostMemory::byte_slice_strat(
|
||||
b.len() as u32,
|
||||
&MemAreas::from([sa_ptr_loc, return_ptr_loc]),
|
||||
),
|
||||
Just(return_ptr_loc),
|
||||
)
|
||||
})
|
||||
.prop_flat_map(|(a, b, c, sa_ptr_loc, sb_ptr_loc, return_ptr_loc)| {
|
||||
(
|
||||
Just(a.clone()),
|
||||
Just(b.clone()),
|
||||
Just(c.clone()),
|
||||
Just(sa_ptr_loc),
|
||||
Just(sb_ptr_loc),
|
||||
HostMemory::byte_slice_strat(
|
||||
c.len() as u32,
|
||||
&MemAreas::from([sa_ptr_loc, sb_ptr_loc, return_ptr_loc]),
|
||||
),
|
||||
Just(return_ptr_loc),
|
||||
)
|
||||
})
|
||||
.prop_map(
|
||||
|(a, b, c, sa_ptr_loc, sb_ptr_loc, sc_ptr_loc, return_ptr_loc)| {
|
||||
MultiStringExercise {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
sa_ptr_loc,
|
||||
sb_ptr_loc,
|
||||
sc_ptr_loc,
|
||||
return_ptr_loc,
|
||||
}
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let write_string = |val: &str, loc: MemArea| {
|
||||
let ptr = host_memory.ptr::<str>((loc.ptr, val.len() as u32));
|
||||
for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) {
|
||||
slot.expect("should be valid pointer")
|
||||
.write(byte)
|
||||
.expect("failed to write");
|
||||
}
|
||||
};
|
||||
|
||||
write_string(&self.a, self.sa_ptr_loc);
|
||||
write_string(&self.b, self.sb_ptr_loc);
|
||||
write_string(&self.c, self.sc_ptr_loc);
|
||||
|
||||
let res = strings::multi_string(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.sa_ptr_loc.ptr as i32,
|
||||
self.a.len() as i32,
|
||||
self.sb_ptr_loc.ptr as i32,
|
||||
self.b.len() as i32,
|
||||
self.sc_ptr_loc.ptr as i32,
|
||||
self.c.len() as i32,
|
||||
self.return_ptr_loc.ptr as i32,
|
||||
);
|
||||
assert_eq!(res, types::Errno::Ok.into(), "multi string errno");
|
||||
|
||||
let given = host_memory
|
||||
.ptr::<u32>(self.return_ptr_loc.ptr)
|
||||
.read()
|
||||
.expect("deref ptr to return value");
|
||||
assert_eq!((self.a.len() + self.b.len() + self.c.len()) as u32, given);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn multi_string(e in MultiStringExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
16
crates/wiggle/tests/strings.witx
Normal file
16
crates/wiggle/tests/strings.witx
Normal file
@@ -0,0 +1,16 @@
|
||||
(use "errno.witx")
|
||||
(module $strings
|
||||
(@interface func (export "hello_string")
|
||||
(param $a_string string)
|
||||
(result $error $errno)
|
||||
(result $total_bytes u32)
|
||||
)
|
||||
|
||||
(@interface func (export "multi_string")
|
||||
(param $a string)
|
||||
(param $b string)
|
||||
(param $c string)
|
||||
(result $error $errno)
|
||||
(result $total_bytes u32)
|
||||
)
|
||||
)
|
||||
422
crates/wiggle/tests/structs.rs
Normal file
422
crates/wiggle/tests/structs.rs
Normal file
@@ -0,0 +1,422 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/structs.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
impl<'a> structs::Structs for WasiCtx<'a> {
|
||||
fn sum_of_pair(&self, an_pair: &types::PairInts) -> Result<i64, types::Errno> {
|
||||
Ok(an_pair.first as i64 + an_pair.second as i64)
|
||||
}
|
||||
|
||||
fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result<i64, types::Errno> {
|
||||
let first = an_pair
|
||||
.first
|
||||
.read()
|
||||
.expect("dereferencing GuestPtr should succeed");
|
||||
let second = an_pair
|
||||
.second
|
||||
.read()
|
||||
.expect("dereferncing GuestPtr should succeed");
|
||||
Ok(first as i64 + second as i64)
|
||||
}
|
||||
|
||||
fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result<i64, types::Errno> {
|
||||
let first = an_pair
|
||||
.first
|
||||
.read()
|
||||
.expect("dereferencing GuestPtr should succeed");
|
||||
let second = an_pair.second as i64;
|
||||
Ok(first as i64 + second)
|
||||
}
|
||||
|
||||
fn return_pair_ints(&self) -> Result<types::PairInts, types::Errno> {
|
||||
Ok(types::PairInts {
|
||||
first: 10,
|
||||
second: 20,
|
||||
})
|
||||
}
|
||||
|
||||
fn return_pair_of_ptrs<'b>(
|
||||
&self,
|
||||
first: GuestPtr<'b, i32>,
|
||||
second: GuestPtr<'b, i32>,
|
||||
) -> Result<types::PairIntPtrs<'b>, types::Errno> {
|
||||
Ok(types::PairIntPtrs { first, second })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SumOfPairExercise {
|
||||
pub input: types::PairInts,
|
||||
pub input_loc: MemArea,
|
||||
pub return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl SumOfPairExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
prop::num::i32::ANY,
|
||||
prop::num::i32::ANY,
|
||||
HostMemory::mem_area_strat(8),
|
||||
HostMemory::mem_area_strat(8),
|
||||
)
|
||||
.prop_map(|(first, second, input_loc, return_loc)| SumOfPairExercise {
|
||||
input: types::PairInts { first, second },
|
||||
input_loc,
|
||||
return_loc,
|
||||
})
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.input_loc, e.return_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr)
|
||||
.write(self.input.first)
|
||||
.expect("input ref_mut");
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr + 4)
|
||||
.write(self.input.second)
|
||||
.expect("input ref_mut");
|
||||
let sum_err = structs::sum_of_pair(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno");
|
||||
|
||||
let return_val: i64 = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref");
|
||||
|
||||
assert_eq!(
|
||||
return_val,
|
||||
self.input.first as i64 + self.input.second as i64,
|
||||
"sum return value"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn sum_of_pair(e in SumOfPairExercise::strat()) {
|
||||
e.test();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SumPairPtrsExercise {
|
||||
input_first: i32,
|
||||
input_second: i32,
|
||||
input_first_loc: MemArea,
|
||||
input_second_loc: MemArea,
|
||||
input_struct_loc: MemArea,
|
||||
return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl SumPairPtrsExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
prop::num::i32::ANY,
|
||||
prop::num::i32::ANY,
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(8),
|
||||
HostMemory::mem_area_strat(8),
|
||||
)
|
||||
.prop_map(
|
||||
|(
|
||||
input_first,
|
||||
input_second,
|
||||
input_first_loc,
|
||||
input_second_loc,
|
||||
input_struct_loc,
|
||||
return_loc,
|
||||
)| SumPairPtrsExercise {
|
||||
input_first,
|
||||
input_second,
|
||||
input_first_loc,
|
||||
input_second_loc,
|
||||
input_struct_loc,
|
||||
return_loc,
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[
|
||||
e.input_first_loc,
|
||||
e.input_second_loc,
|
||||
e.input_struct_loc,
|
||||
e.return_loc,
|
||||
])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
.ptr(self.input_first_loc.ptr)
|
||||
.write(self.input_first)
|
||||
.expect("input_first ref");
|
||||
host_memory
|
||||
.ptr(self.input_second_loc.ptr)
|
||||
.write(self.input_second)
|
||||
.expect("input_second ref");
|
||||
|
||||
host_memory
|
||||
.ptr(self.input_struct_loc.ptr)
|
||||
.write(self.input_first_loc.ptr)
|
||||
.expect("input_struct ref");
|
||||
host_memory
|
||||
.ptr(self.input_struct_loc.ptr + 4)
|
||||
.write(self.input_second_loc.ptr)
|
||||
.expect("input_struct ref");
|
||||
|
||||
let res = structs::sum_of_pair_of_ptrs(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_struct_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno");
|
||||
|
||||
let doubled: i64 = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref");
|
||||
|
||||
assert_eq!(
|
||||
doubled,
|
||||
(self.input_first as i64) + (self.input_second as i64),
|
||||
"sum of pair of ptrs return val"
|
||||
);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn sum_of_pair_of_ptrs(e in SumPairPtrsExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SumIntAndPtrExercise {
|
||||
input_first: i32,
|
||||
input_second: i32,
|
||||
input_first_loc: MemArea,
|
||||
input_struct_loc: MemArea,
|
||||
return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl SumIntAndPtrExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
prop::num::i32::ANY,
|
||||
prop::num::i32::ANY,
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(8),
|
||||
HostMemory::mem_area_strat(8),
|
||||
)
|
||||
.prop_map(
|
||||
|(input_first, input_second, input_first_loc, input_struct_loc, return_loc)| {
|
||||
SumIntAndPtrExercise {
|
||||
input_first,
|
||||
input_second,
|
||||
input_first_loc,
|
||||
input_struct_loc,
|
||||
return_loc,
|
||||
}
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.input_first_loc, e.input_struct_loc, e.return_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
.ptr(self.input_first_loc.ptr)
|
||||
.write(self.input_first)
|
||||
.expect("input_first ref");
|
||||
host_memory
|
||||
.ptr(self.input_struct_loc.ptr)
|
||||
.write(self.input_first_loc.ptr)
|
||||
.expect("input_struct ref");
|
||||
host_memory
|
||||
.ptr(self.input_struct_loc.ptr + 4)
|
||||
.write(self.input_second)
|
||||
.expect("input_struct ref");
|
||||
|
||||
let res = structs::sum_of_int_and_ptr(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_struct_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
assert_eq!(res, types::Errno::Ok.into(), "sum of int and ptr errno");
|
||||
|
||||
let doubled: i64 = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref");
|
||||
|
||||
assert_eq!(
|
||||
doubled,
|
||||
(self.input_first as i64) + (self.input_second as i64),
|
||||
"sum of pair of ptrs return val"
|
||||
);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn sum_of_int_and_ptr(e in SumIntAndPtrExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReturnPairInts {
|
||||
pub return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl ReturnPairInts {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
HostMemory::mem_area_strat(8)
|
||||
.prop_map(|return_loc| ReturnPairInts { return_loc })
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let err = structs::return_pair_ints(&ctx, &host_memory, self.return_loc.ptr as i32);
|
||||
|
||||
assert_eq!(err, types::Errno::Ok.into(), "return struct errno");
|
||||
|
||||
let return_struct: types::PairInts = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref");
|
||||
|
||||
assert_eq!(
|
||||
return_struct,
|
||||
types::PairInts {
|
||||
first: 10,
|
||||
second: 20
|
||||
},
|
||||
"return_pair_ints return value"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn return_pair_ints(e in ReturnPairInts::strat()) {
|
||||
e.test();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReturnPairPtrsExercise {
|
||||
input_first: i32,
|
||||
input_second: i32,
|
||||
input_first_loc: MemArea,
|
||||
input_second_loc: MemArea,
|
||||
return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl ReturnPairPtrsExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
prop::num::i32::ANY,
|
||||
prop::num::i32::ANY,
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(4),
|
||||
HostMemory::mem_area_strat(8),
|
||||
)
|
||||
.prop_map(
|
||||
|(input_first, input_second, input_first_loc, input_second_loc, return_loc)| {
|
||||
ReturnPairPtrsExercise {
|
||||
input_first,
|
||||
input_second,
|
||||
input_first_loc,
|
||||
input_second_loc,
|
||||
return_loc,
|
||||
}
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.input_first_loc, e.input_second_loc, e.return_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
host_memory
|
||||
.ptr(self.input_first_loc.ptr)
|
||||
.write(self.input_first)
|
||||
.expect("input_first ref");
|
||||
host_memory
|
||||
.ptr(self.input_second_loc.ptr)
|
||||
.write(self.input_second)
|
||||
.expect("input_second ref");
|
||||
|
||||
let res = structs::return_pair_of_ptrs(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_first_loc.ptr as i32,
|
||||
self.input_second_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno");
|
||||
|
||||
let ptr_pair_int_ptrs: types::PairIntPtrs<'_> = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("failed to read return location");
|
||||
let ret_first_ptr = ptr_pair_int_ptrs.first;
|
||||
let ret_second_ptr = ptr_pair_int_ptrs.second;
|
||||
assert_eq!(
|
||||
self.input_first,
|
||||
ret_first_ptr
|
||||
.read()
|
||||
.expect("deref extracted ptr to first element")
|
||||
);
|
||||
assert_eq!(
|
||||
self.input_second,
|
||||
ret_second_ptr
|
||||
.read()
|
||||
.expect("deref extracted ptr to second element")
|
||||
);
|
||||
}
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn return_pair_of_ptrs(e in ReturnPairPtrsExercise::strat()) {
|
||||
e.test()
|
||||
}
|
||||
}
|
||||
40
crates/wiggle/tests/structs.witx
Normal file
40
crates/wiggle/tests/structs.witx
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
(use "errno.witx")
|
||||
|
||||
(typename $pair_ints
|
||||
(struct
|
||||
(field $first s32)
|
||||
(field $second s32)))
|
||||
|
||||
(typename $pair_int_ptrs
|
||||
(struct
|
||||
(field $first (@witx const_pointer s32))
|
||||
(field $second (@witx const_pointer s32))))
|
||||
|
||||
(typename $pair_int_and_ptr
|
||||
(struct
|
||||
(field $first (@witx const_pointer s32))
|
||||
(field $second s32)))
|
||||
|
||||
(module $structs
|
||||
(@interface func (export "sum_of_pair")
|
||||
(param $an_pair $pair_ints)
|
||||
(result $error $errno)
|
||||
(result $doubled s64))
|
||||
(@interface func (export "sum_of_pair_of_ptrs")
|
||||
(param $an_pair $pair_int_ptrs)
|
||||
(result $error $errno)
|
||||
(result $doubled s64))
|
||||
(@interface func (export "sum_of_int_and_ptr")
|
||||
(param $an_pair $pair_int_and_ptr)
|
||||
(result $error $errno)
|
||||
(result $double s64))
|
||||
(@interface func (export "return_pair_ints")
|
||||
(result $error $errno)
|
||||
(result $an_pair $pair_ints))
|
||||
(@interface func (export "return_pair_of_ptrs")
|
||||
(param $first (@witx const_pointer s32))
|
||||
(param $second (@witx const_pointer s32))
|
||||
(result $error $errno)
|
||||
(result $an_pair $pair_int_ptrs))
|
||||
)
|
||||
746
crates/wiggle/tests/typenames.witx
Normal file
746
crates/wiggle/tests/typenames.witx
Normal file
@@ -0,0 +1,746 @@
|
||||
;; Type names used by low-level WASI interfaces.
|
||||
;;
|
||||
;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi).
|
||||
;;
|
||||
;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/master/docs/witx.md)
|
||||
;; for an explanation of what that means.
|
||||
|
||||
(typename $size u32)
|
||||
|
||||
;;; Non-negative file size or length of a region within a file.
|
||||
(typename $filesize u64)
|
||||
|
||||
;;; Timestamp in nanoseconds.
|
||||
(typename $timestamp u64)
|
||||
|
||||
;;; Identifiers for clocks.
|
||||
(typename $clockid
|
||||
(enum u32
|
||||
;;; The clock measuring real time. Time value zero corresponds with
|
||||
;;; 1970-01-01T00:00:00Z.
|
||||
$realtime
|
||||
;;; The store-wide monotonic clock, which is defined as a clock measuring
|
||||
;;; real time, whose value cannot be adjusted and which cannot have negative
|
||||
;;; clock jumps. The epoch of this clock is undefined. The absolute time
|
||||
;;; value of this clock therefore has no meaning.
|
||||
$monotonic
|
||||
;;; The CPU-time clock associated with the current process.
|
||||
$process_cputime_id
|
||||
;;; The CPU-time clock associated with the current thread.
|
||||
$thread_cputime_id
|
||||
)
|
||||
)
|
||||
|
||||
;;; Error codes returned by functions.
|
||||
;;; Not all of these error codes are returned by the functions provided by this
|
||||
;;; API; some are used in higher-level library layers, and others are provided
|
||||
;;; merely for alignment with POSIX.
|
||||
(typename $errno
|
||||
(enum u16
|
||||
;;; No error occurred. System call completed successfully.
|
||||
$success
|
||||
;;; Argument list too long.
|
||||
$2big
|
||||
;;; Permission denied.
|
||||
$acces
|
||||
;;; Address in use.
|
||||
$addrinuse
|
||||
;;; Address not available.
|
||||
$addrnotavail
|
||||
;;; Address family not supported.
|
||||
$afnosupport
|
||||
;;; Resource unavailable, or operation would block.
|
||||
$again
|
||||
;;; Connection already in progress.
|
||||
$already
|
||||
;;; Bad file descriptor.
|
||||
$badf
|
||||
;;; Bad message.
|
||||
$badmsg
|
||||
;;; Device or resource busy.
|
||||
$busy
|
||||
;;; Operation canceled.
|
||||
$canceled
|
||||
;;; No child processes.
|
||||
$child
|
||||
;;; Connection aborted.
|
||||
$connaborted
|
||||
;;; Connection refused.
|
||||
$connrefused
|
||||
;;; Connection reset.
|
||||
$connreset
|
||||
;;; Resource deadlock would occur.
|
||||
$deadlk
|
||||
;;; Destination address required.
|
||||
$destaddrreq
|
||||
;;; Mathematics argument out of domain of function.
|
||||
$dom
|
||||
;;; Reserved.
|
||||
$dquot
|
||||
;;; File exists.
|
||||
$exist
|
||||
;;; Bad address.
|
||||
$fault
|
||||
;;; File too large.
|
||||
$fbig
|
||||
;;; Host is unreachable.
|
||||
$hostunreach
|
||||
;;; Identifier removed.
|
||||
$idrm
|
||||
;;; Illegal byte sequence.
|
||||
$ilseq
|
||||
;;; Operation in progress.
|
||||
$inprogress
|
||||
;;; Interrupted function.
|
||||
$intr
|
||||
;;; Invalid argument.
|
||||
$inval
|
||||
;;; I/O error.
|
||||
$io
|
||||
;;; Socket is connected.
|
||||
$isconn
|
||||
;;; Is a directory.
|
||||
$isdir
|
||||
;;; Too many levels of symbolic links.
|
||||
$loop
|
||||
;;; File descriptor value too large.
|
||||
$mfile
|
||||
;;; Too many links.
|
||||
$mlink
|
||||
;;; Message too large.
|
||||
$msgsize
|
||||
;;; Reserved.
|
||||
$multihop
|
||||
;;; Filename too long.
|
||||
$nametoolong
|
||||
;;; Network is down.
|
||||
$netdown
|
||||
;;; Connection aborted by network.
|
||||
$netreset
|
||||
;;; Network unreachable.
|
||||
$netunreach
|
||||
;;; Too many files open in system.
|
||||
$nfile
|
||||
;;; No buffer space available.
|
||||
$nobufs
|
||||
;;; No such device.
|
||||
$nodev
|
||||
;;; No such file or directory.
|
||||
$noent
|
||||
;;; Executable file format error.
|
||||
$noexec
|
||||
;;; No locks available.
|
||||
$nolck
|
||||
;;; Reserved.
|
||||
$nolink
|
||||
;;; Not enough space.
|
||||
$nomem
|
||||
;;; No message of the desired type.
|
||||
$nomsg
|
||||
;;; Protocol not available.
|
||||
$noprotoopt
|
||||
;;; No space left on device.
|
||||
$nospc
|
||||
;;; Function not supported.
|
||||
$nosys
|
||||
;;; The socket is not connected.
|
||||
$notconn
|
||||
;;; Not a directory or a symbolic link to a directory.
|
||||
$notdir
|
||||
;;; Directory not empty.
|
||||
$notempty
|
||||
;;; State not recoverable.
|
||||
$notrecoverable
|
||||
;;; Not a socket.
|
||||
$notsock
|
||||
;;; Not supported, or operation not supported on socket.
|
||||
$notsup
|
||||
;;; Inappropriate I/O control operation.
|
||||
$notty
|
||||
;;; No such device or address.
|
||||
$nxio
|
||||
;;; Value too large to be stored in data type.
|
||||
$overflow
|
||||
;;; Previous owner died.
|
||||
$ownerdead
|
||||
;;; Operation not permitted.
|
||||
$perm
|
||||
;;; Broken pipe.
|
||||
$pipe
|
||||
;;; Protocol error.
|
||||
$proto
|
||||
;;; Protocol not supported.
|
||||
$protonosupport
|
||||
;;; Protocol wrong type for socket.
|
||||
$prototype
|
||||
;;; Result too large.
|
||||
$range
|
||||
;;; Read-only file system.
|
||||
$rofs
|
||||
;;; Invalid seek.
|
||||
$spipe
|
||||
;;; No such process.
|
||||
$srch
|
||||
;;; Reserved.
|
||||
$stale
|
||||
;;; Connection timed out.
|
||||
$timedout
|
||||
;;; Text file busy.
|
||||
$txtbsy
|
||||
;;; Cross-device link.
|
||||
$xdev
|
||||
;;; Extension: Capabilities insufficient.
|
||||
$notcapable
|
||||
)
|
||||
)
|
||||
|
||||
;;; File descriptor rights, determining which actions may be performed.
|
||||
(typename $rights
|
||||
(flags u64
|
||||
;;; The right to invoke `fd_datasync`.
|
||||
;;
|
||||
;;; If `path_open` is set, includes the right to invoke
|
||||
;;; `path_open` with `fdflags::dsync`.
|
||||
$fd_datasync
|
||||
;;; The right to invoke `fd_read` and `sock_recv`.
|
||||
;;
|
||||
;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pread`.
|
||||
$fd_read
|
||||
;;; The right to invoke `fd_seek`. This flag implies `rights::fd_tell`.
|
||||
$fd_seek
|
||||
;;; The right to invoke `fd_fdstat_set_flags`.
|
||||
$fd_fdstat_set_flags
|
||||
;;; The right to invoke `fd_sync`.
|
||||
;;
|
||||
;;; If `path_open` is set, includes the right to invoke
|
||||
;;; `path_open` with `fdflags::rsync` and `fdflags::dsync`.
|
||||
$fd_sync
|
||||
;;; The right to invoke `fd_seek` in such a way that the file offset
|
||||
;;; remains unaltered (i.e., `whence::cur` with offset zero), or to
|
||||
;;; invoke `fd_tell`.
|
||||
$fd_tell
|
||||
;;; The right to invoke `fd_write` and `sock_send`.
|
||||
;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`.
|
||||
$fd_write
|
||||
;;; The right to invoke `fd_advise`.
|
||||
$fd_advise
|
||||
;;; The right to invoke `fd_allocate`.
|
||||
$fd_allocate
|
||||
;;; The right to invoke `path_create_directory`.
|
||||
$path_create_directory
|
||||
;;; If `path_open` is set, the right to invoke `path_open` with `oflags::creat`.
|
||||
$path_create_file
|
||||
;;; The right to invoke `path_link` with the file descriptor as the
|
||||
;;; source directory.
|
||||
$path_link_source
|
||||
;;; The right to invoke `path_link` with the file descriptor as the
|
||||
;;; target directory.
|
||||
$path_link_target
|
||||
;;; The right to invoke `path_open`.
|
||||
$path_open
|
||||
;;; The right to invoke `fd_readdir`.
|
||||
$fd_readdir
|
||||
;;; The right to invoke `path_readlink`.
|
||||
$path_readlink
|
||||
;;; The right to invoke `path_rename` with the file descriptor as the source directory.
|
||||
$path_rename_source
|
||||
;;; The right to invoke `path_rename` with the file descriptor as the target directory.
|
||||
$path_rename_target
|
||||
;;; The right to invoke `path_filestat_get`.
|
||||
$path_filestat_get
|
||||
;;; The right to change a file's size (there is no `path_filestat_set_size`).
|
||||
;;; If `path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`.
|
||||
$path_filestat_set_size
|
||||
;;; The right to invoke `path_filestat_set_times`.
|
||||
$path_filestat_set_times
|
||||
;;; The right to invoke `fd_filestat_get`.
|
||||
$fd_filestat_get
|
||||
;;; The right to invoke `fd_filestat_set_size`.
|
||||
$fd_filestat_set_size
|
||||
;;; The right to invoke `fd_filestat_set_times`.
|
||||
$fd_filestat_set_times
|
||||
;;; The right to invoke `path_symlink`.
|
||||
$path_symlink
|
||||
;;; The right to invoke `path_remove_directory`.
|
||||
$path_remove_directory
|
||||
;;; The right to invoke `path_unlink_file`.
|
||||
$path_unlink_file
|
||||
;;; If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`.
|
||||
;;; If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`.
|
||||
$poll_fd_readwrite
|
||||
;;; The right to invoke `sock_shutdown`.
|
||||
$sock_shutdown
|
||||
)
|
||||
)
|
||||
|
||||
;;; A file descriptor handle.
|
||||
(typename $fd (handle))
|
||||
|
||||
;;; A region of memory for scatter/gather reads.
|
||||
(typename $iovec
|
||||
(struct
|
||||
;;; The address of the buffer to be filled.
|
||||
(field $buf (@witx pointer u8))
|
||||
;;; The length of the buffer to be filled.
|
||||
(field $buf_len $size)
|
||||
)
|
||||
)
|
||||
|
||||
;;; A region of memory for scatter/gather writes.
|
||||
(typename $ciovec
|
||||
(struct
|
||||
;;; The address of the buffer to be written.
|
||||
(field $buf (@witx const_pointer u8))
|
||||
;;; The length of the buffer to be written.
|
||||
(field $buf_len $size)
|
||||
)
|
||||
)
|
||||
|
||||
(typename $iovec_array (array $iovec))
|
||||
(typename $ciovec_array (array $ciovec))
|
||||
|
||||
;;; Relative offset within a file.
|
||||
(typename $filedelta s64)
|
||||
|
||||
;;; The position relative to which to set the offset of the file descriptor.
|
||||
(typename $whence
|
||||
(enum u8
|
||||
;;; Seek relative to start-of-file.
|
||||
$set
|
||||
;;; Seek relative to current position.
|
||||
$cur
|
||||
;;; Seek relative to end-of-file.
|
||||
$end
|
||||
)
|
||||
)
|
||||
|
||||
;;; A reference to the offset of a directory entry.
|
||||
;;;
|
||||
;;; The value 0 signifies the start of the directory.
|
||||
(typename $dircookie u64)
|
||||
|
||||
;;; The type for the $d_namlen field of $dirent.
|
||||
(typename $dirnamlen u32)
|
||||
|
||||
;;; File serial number that is unique within its file system.
|
||||
(typename $inode u64)
|
||||
|
||||
;;; The type of a file descriptor or file.
|
||||
(typename $filetype
|
||||
(enum u8
|
||||
;;; The type of the file descriptor or file is unknown or is different from any of the other types specified.
|
||||
$unknown
|
||||
;;; The file descriptor or file refers to a block device inode.
|
||||
$block_device
|
||||
;;; The file descriptor or file refers to a character device inode.
|
||||
$character_device
|
||||
;;; The file descriptor or file refers to a directory inode.
|
||||
$directory
|
||||
;;; The file descriptor or file refers to a regular file inode.
|
||||
$regular_file
|
||||
;;; The file descriptor or file refers to a datagram socket.
|
||||
$socket_dgram
|
||||
;;; The file descriptor or file refers to a byte-stream socket.
|
||||
$socket_stream
|
||||
;;; The file refers to a symbolic link inode.
|
||||
$symbolic_link
|
||||
)
|
||||
)
|
||||
|
||||
;;; A directory entry.
|
||||
(typename $dirent
|
||||
(struct
|
||||
;;; The offset of the next directory entry stored in this directory.
|
||||
(field $d_next $dircookie)
|
||||
;;; The serial number of the file referred to by this directory entry.
|
||||
(field $d_ino $inode)
|
||||
;;; The length of the name of the directory entry.
|
||||
(field $d_namlen $dirnamlen)
|
||||
;;; The type of the file referred to by this directory entry.
|
||||
(field $d_type $filetype)
|
||||
)
|
||||
)
|
||||
|
||||
;;; File or memory access pattern advisory information.
|
||||
(typename $advice
|
||||
(enum u8
|
||||
;;; The application has no advice to give on its behavior with respect to the specified data.
|
||||
$normal
|
||||
;;; The application expects to access the specified data sequentially from lower offsets to higher offsets.
|
||||
$sequential
|
||||
;;; The application expects to access the specified data in a random order.
|
||||
$random
|
||||
;;; The application expects to access the specified data in the near future.
|
||||
$willneed
|
||||
;;; The application expects that it will not access the specified data in the near future.
|
||||
$dontneed
|
||||
;;; The application expects to access the specified data once and then not reuse it thereafter.
|
||||
$noreuse
|
||||
)
|
||||
)
|
||||
|
||||
;;; File descriptor flags.
|
||||
(typename $fdflags
|
||||
(flags u16
|
||||
;;; Append mode: Data written to the file is always appended to the file's end.
|
||||
$append
|
||||
;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized.
|
||||
$dsync
|
||||
;;; Non-blocking mode.
|
||||
$nonblock
|
||||
;;; Synchronized read I/O operations.
|
||||
$rsync
|
||||
;;; Write according to synchronized I/O file integrity completion. In
|
||||
;;; addition to synchronizing the data stored in the file, the implementation
|
||||
;;; may also synchronously update the file's metadata.
|
||||
$sync
|
||||
)
|
||||
)
|
||||
|
||||
;;; File descriptor attributes.
|
||||
(typename $fdstat
|
||||
(struct
|
||||
;;; File type.
|
||||
(field $fs_filetype $filetype)
|
||||
;;; File descriptor flags.
|
||||
(field $fs_flags $fdflags)
|
||||
;;; Rights that apply to this file descriptor.
|
||||
(field $fs_rights_base $rights)
|
||||
;;; Maximum set of rights that may be installed on new file descriptors that
|
||||
;;; are created through this file descriptor, e.g., through `path_open`.
|
||||
(field $fs_rights_inheriting $rights)
|
||||
)
|
||||
)
|
||||
|
||||
;;; Identifier for a device containing a file system. Can be used in combination
|
||||
;;; with `inode` to uniquely identify a file or directory in the filesystem.
|
||||
(typename $device u64)
|
||||
|
||||
;;; Which file time attributes to adjust.
|
||||
(typename $fstflags
|
||||
(flags u16
|
||||
;;; Adjust the last data access timestamp to the value stored in `filestat::atim`.
|
||||
$atim
|
||||
;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`.
|
||||
$atim_now
|
||||
;;; Adjust the last data modification timestamp to the value stored in `filestat::mtim`.
|
||||
$mtim
|
||||
;;; Adjust the last data modification timestamp to the time of clock `clockid::realtime`.
|
||||
$mtim_now
|
||||
)
|
||||
)
|
||||
|
||||
;;; Flags determining the method of how paths are resolved.
|
||||
(typename $lookupflags
|
||||
(flags u32
|
||||
;;; As long as the resolved path corresponds to a symbolic link, it is expanded.
|
||||
$symlink_follow
|
||||
)
|
||||
)
|
||||
|
||||
;;; Open flags used by `path_open`.
|
||||
(typename $oflags
|
||||
(flags u16
|
||||
;;; Create file if it does not exist.
|
||||
$creat
|
||||
;;; Fail if not a directory.
|
||||
$directory
|
||||
;;; Fail if file already exists.
|
||||
$excl
|
||||
;;; Truncate file to size 0.
|
||||
$trunc
|
||||
)
|
||||
)
|
||||
|
||||
;;; Number of hard links to an inode.
|
||||
(typename $linkcount u64)
|
||||
|
||||
;;; File attributes.
|
||||
(typename $filestat
|
||||
(struct
|
||||
;;; Device ID of device containing the file.
|
||||
(field $dev $device)
|
||||
;;; File serial number.
|
||||
(field $ino $inode)
|
||||
;;; File type.
|
||||
(field $filetype $filetype)
|
||||
;;; Number of hard links to the file.
|
||||
(field $nlink $linkcount)
|
||||
;;; For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link.
|
||||
(field $size $filesize)
|
||||
;;; Last data access timestamp.
|
||||
(field $atim $timestamp)
|
||||
;;; Last data modification timestamp.
|
||||
(field $mtim $timestamp)
|
||||
;;; Last file status change timestamp.
|
||||
(field $ctim $timestamp)
|
||||
)
|
||||
)
|
||||
|
||||
;;; User-provided value that may be attached to objects that is retained when
|
||||
;;; extracted from the implementation.
|
||||
(typename $userdata u64)
|
||||
|
||||
;;; Type of a subscription to an event or its occurrence.
|
||||
(typename $eventtype
|
||||
(enum u8
|
||||
;;; The time value of clock `subscription_clock::id` has
|
||||
;;; reached timestamp `subscription_clock::timeout`.
|
||||
$clock
|
||||
;;; File descriptor `subscription_fd_readwrite::file_descriptor` has data
|
||||
;;; available for reading. This event always triggers for regular files.
|
||||
$fd_read
|
||||
;;; File descriptor `subscription_fd_readwrite::file_descriptor` has capacity
|
||||
;;; available for writing. This event always triggers for regular files.
|
||||
$fd_write
|
||||
)
|
||||
)
|
||||
|
||||
;;; The state of the file descriptor subscribed to with
|
||||
;;; `eventtype::fd_read` or `eventtype::fd_write`.
|
||||
(typename $eventrwflags
|
||||
(flags u16
|
||||
;;; The peer of this socket has closed or disconnected.
|
||||
$fd_readwrite_hangup
|
||||
)
|
||||
)
|
||||
|
||||
;;; The contents of an $event when type is `eventtype::fd_read` or
|
||||
;;; `eventtype::fd_write`.
|
||||
(typename $event_fd_readwrite
|
||||
(struct
|
||||
;;; The number of bytes available for reading or writing.
|
||||
(field $nbytes $filesize)
|
||||
;;; The state of the file descriptor.
|
||||
(field $flags $eventrwflags)
|
||||
)
|
||||
)
|
||||
|
||||
;;; An event that occurred.
|
||||
(typename $event
|
||||
(struct
|
||||
;;; User-provided value that got attached to `subscription::userdata`.
|
||||
(field $userdata $userdata)
|
||||
;;; If non-zero, an error that occurred while processing the subscription request.
|
||||
(field $error $errno)
|
||||
;;; The type of event that occured
|
||||
(field $type $eventtype)
|
||||
;;; The contents of the event, if it is an `eventtype::fd_read` or
|
||||
;;; `eventtype::fd_write`. `eventtype::clock` events ignore this field.
|
||||
(field $fd_readwrite $event_fd_readwrite)
|
||||
)
|
||||
)
|
||||
|
||||
;;; Flags determining how to interpret the timestamp provided in
|
||||
;;; `subscription_clock::timeout`.
|
||||
(typename $subclockflags
|
||||
(flags u16
|
||||
;;; If set, treat the timestamp provided in
|
||||
;;; `subscription_clock::timeout` as an absolute timestamp of clock
|
||||
;;; `subscription_clock::id`. If clear, treat the timestamp
|
||||
;;; provided in `subscription_clock::timeout` relative to the
|
||||
;;; current time value of clock `subscription_clock::id`.
|
||||
$subscription_clock_abstime
|
||||
)
|
||||
)
|
||||
|
||||
;;; The contents of a `subscription` when type is `eventtype::clock`.
|
||||
(typename $subscription_clock
|
||||
(struct
|
||||
;;; The clock against which to compare the timestamp.
|
||||
(field $id $clockid)
|
||||
;;; The absolute or relative timestamp.
|
||||
(field $timeout $timestamp)
|
||||
;;; The amount of time that the implementation may wait additionally
|
||||
;;; to coalesce with other events.
|
||||
(field $precision $timestamp)
|
||||
;;; Flags specifying whether the timeout is absolute or relative
|
||||
(field $flags $subclockflags)
|
||||
)
|
||||
)
|
||||
|
||||
;;; The contents of a `subscription` when type is type is
|
||||
;;; `eventtype::fd_read` or `eventtype::fd_write`.
|
||||
(typename $subscription_fd_readwrite
|
||||
(struct
|
||||
;;; The file descriptor on which to wait for it to become ready for reading or writing.
|
||||
(field $file_descriptor $fd)
|
||||
)
|
||||
)
|
||||
|
||||
;;; The contents of a `subscription`.
|
||||
(typename $subscription_u
|
||||
(union $eventtype
|
||||
(field $clock $subscription_clock)
|
||||
(field $fd_read $subscription_fd_readwrite)
|
||||
(field $fd_write $subscription_fd_readwrite)
|
||||
)
|
||||
)
|
||||
|
||||
;;; Subscription to an event.
|
||||
(typename $subscription
|
||||
(struct
|
||||
;;; User-provided value that is attached to the subscription in the
|
||||
;;; implementation and returned through `event::userdata`.
|
||||
(field $userdata $userdata)
|
||||
;;; The type of the event to which to subscribe, and its contents
|
||||
(field $u $subscription_u)
|
||||
)
|
||||
)
|
||||
|
||||
;;; Exit code generated by a process when exiting.
|
||||
(typename $exitcode u32)
|
||||
|
||||
;;; Signal condition.
|
||||
(typename $signal
|
||||
(enum u8
|
||||
;;; No signal. Note that POSIX has special semantics for `kill(pid, 0)`,
|
||||
;;; so this value is reserved.
|
||||
$none
|
||||
;;; Hangup.
|
||||
;;; Action: Terminates the process.
|
||||
$hup
|
||||
;;; Terminate interrupt signal.
|
||||
;;; Action: Terminates the process.
|
||||
$int
|
||||
;;; Terminal quit signal.
|
||||
;;; Action: Terminates the process.
|
||||
$quit
|
||||
;;; Illegal instruction.
|
||||
;;; Action: Terminates the process.
|
||||
$ill
|
||||
;;; Trace/breakpoint trap.
|
||||
;;; Action: Terminates the process.
|
||||
$trap
|
||||
;;; Process abort signal.
|
||||
;;; Action: Terminates the process.
|
||||
$abrt
|
||||
;;; Access to an undefined portion of a memory object.
|
||||
;;; Action: Terminates the process.
|
||||
$bus
|
||||
;;; Erroneous arithmetic operation.
|
||||
;;; Action: Terminates the process.
|
||||
$fpe
|
||||
;;; Kill.
|
||||
;;; Action: Terminates the process.
|
||||
$kill
|
||||
;;; User-defined signal 1.
|
||||
;;; Action: Terminates the process.
|
||||
$usr1
|
||||
;;; Invalid memory reference.
|
||||
;;; Action: Terminates the process.
|
||||
$segv
|
||||
;;; User-defined signal 2.
|
||||
;;; Action: Terminates the process.
|
||||
$usr2
|
||||
;;; Write on a pipe with no one to read it.
|
||||
;;; Action: Ignored.
|
||||
$pipe
|
||||
;;; Alarm clock.
|
||||
;;; Action: Terminates the process.
|
||||
$alrm
|
||||
;;; Termination signal.
|
||||
;;; Action: Terminates the process.
|
||||
$term
|
||||
;;; Child process terminated, stopped, or continued.
|
||||
;;; Action: Ignored.
|
||||
$chld
|
||||
;;; Continue executing, if stopped.
|
||||
;;; Action: Continues executing, if stopped.
|
||||
$cont
|
||||
;;; Stop executing.
|
||||
;;; Action: Stops executing.
|
||||
$stop
|
||||
;;; Terminal stop signal.
|
||||
;;; Action: Stops executing.
|
||||
$tstp
|
||||
;;; Background process attempting read.
|
||||
;;; Action: Stops executing.
|
||||
$ttin
|
||||
;;; Background process attempting write.
|
||||
;;; Action: Stops executing.
|
||||
$ttou
|
||||
;;; High bandwidth data is available at a socket.
|
||||
;;; Action: Ignored.
|
||||
$urg
|
||||
;;; CPU time limit exceeded.
|
||||
;;; Action: Terminates the process.
|
||||
$xcpu
|
||||
;;; File size limit exceeded.
|
||||
;;; Action: Terminates the process.
|
||||
$xfsz
|
||||
;;; Virtual timer expired.
|
||||
;;; Action: Terminates the process.
|
||||
$vtalrm
|
||||
;;; Profiling timer expired.
|
||||
;;; Action: Terminates the process.
|
||||
$prof
|
||||
;;; Window changed.
|
||||
;;; Action: Ignored.
|
||||
$winch
|
||||
;;; I/O possible.
|
||||
;;; Action: Terminates the process.
|
||||
$poll
|
||||
;;; Power failure.
|
||||
;;; Action: Terminates the process.
|
||||
$pwr
|
||||
;;; Bad system call.
|
||||
;;; Action: Terminates the process.
|
||||
$sys
|
||||
)
|
||||
)
|
||||
|
||||
;;; Flags provided to `sock_recv`.
|
||||
(typename $riflags
|
||||
(flags u16
|
||||
;;; Returns the message without removing it from the socket's receive queue.
|
||||
$recv_peek
|
||||
;;; On byte-stream sockets, block until the full amount of data can be returned.
|
||||
$recv_waitall
|
||||
)
|
||||
)
|
||||
|
||||
;;; Flags returned by `sock_recv`.
|
||||
(typename $roflags
|
||||
(flags u16
|
||||
;;; Returned by `sock_recv`: Message data has been truncated.
|
||||
$recv_data_truncated
|
||||
)
|
||||
)
|
||||
|
||||
;;; Flags provided to `sock_send`. As there are currently no flags
|
||||
;;; defined, it must be set to zero.
|
||||
(typename $siflags u16)
|
||||
|
||||
;;; Which channels on a socket to shut down.
|
||||
(typename $sdflags
|
||||
(flags u8
|
||||
;;; Disables further receive operations.
|
||||
$rd
|
||||
;;; Disables further send operations.
|
||||
$wr
|
||||
)
|
||||
)
|
||||
|
||||
;;; Identifiers for preopened capabilities.
|
||||
(typename $preopentype
|
||||
(enum u8
|
||||
;;; A pre-opened directory.
|
||||
$dir
|
||||
)
|
||||
)
|
||||
|
||||
;;; The contents of a $prestat when type is `preopentype::dir`.
|
||||
(typename $prestat_dir
|
||||
(struct
|
||||
;;; The length of the directory name for use with `fd_prestat_dir_name`.
|
||||
(field $pr_name_len $size)
|
||||
)
|
||||
)
|
||||
|
||||
;;; Information about a pre-opened capability.
|
||||
(typename $prestat
|
||||
(union $preopentype
|
||||
(field $dir $prestat_dir)
|
||||
)
|
||||
)
|
||||
255
crates/wiggle/tests/union.rs
Normal file
255
crates/wiggle/tests/union.rs
Normal file
@@ -0,0 +1,255 @@
|
||||
use proptest::prelude::*;
|
||||
use wiggle_runtime::{GuestError, GuestMemory, GuestType};
|
||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/union.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
impl_errno!(types::Errno);
|
||||
|
||||
// Avoid panics on overflow
|
||||
fn mult_lose_overflow(a: i32, b: u32) -> i32 {
|
||||
let a_64: i64 = a as i64;
|
||||
let b_64: i64 = b as i64;
|
||||
let product = a_64 * b_64;
|
||||
product as i32
|
||||
}
|
||||
|
||||
// Avoid assert_eq(NaN, NaN) failures
|
||||
fn mult_zero_nan(a: f32, b: u32) -> f32 {
|
||||
if a.is_nan() {
|
||||
0.0
|
||||
} else {
|
||||
let product = a * b as f32;
|
||||
if product.is_nan() {
|
||||
0.0
|
||||
} else {
|
||||
product
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> union_example::UnionExample for WasiCtx<'a> {
|
||||
fn get_tag(&self, u: &types::Reason) -> Result<types::Excuse, types::Errno> {
|
||||
println!("GET TAG: {:?}", u);
|
||||
match u {
|
||||
types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte),
|
||||
types::Reason::Traffic { .. } => Ok(types::Excuse::Traffic),
|
||||
types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping),
|
||||
}
|
||||
}
|
||||
fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> {
|
||||
match u {
|
||||
types::ReasonMut::DogAte(fptr) => {
|
||||
let val = fptr.read().expect("valid pointer");
|
||||
println!("REASON MULT DogAte({})", val);
|
||||
fptr.write(mult_zero_nan(val, multiply_by))
|
||||
.expect("valid pointer");
|
||||
}
|
||||
types::ReasonMut::Traffic(iptr) => {
|
||||
let val = iptr.read().expect("valid pointer");
|
||||
println!("REASON MULT Traffic({})", val);
|
||||
iptr.write(mult_lose_overflow(val, multiply_by))
|
||||
.expect("valid pointer");
|
||||
}
|
||||
types::ReasonMut::Sleeping => {
|
||||
println!("REASON MULT Sleeping");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn reason_strat() -> impl Strategy<Value = types::Reason> {
|
||||
prop_oneof![
|
||||
prop::num::f32::ANY.prop_map(|v| types::Reason::DogAte(v)),
|
||||
prop::num::i32::ANY.prop_map(|v| types::Reason::Traffic(v)),
|
||||
Just(types::Reason::Sleeping),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn reason_tag(r: &types::Reason) -> types::Excuse {
|
||||
match r {
|
||||
types::Reason::DogAte { .. } => types::Excuse::DogAte,
|
||||
types::Reason::Traffic { .. } => types::Excuse::Traffic,
|
||||
types::Reason::Sleeping { .. } => types::Excuse::Sleeping,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GetTagExercise {
|
||||
pub input: types::Reason,
|
||||
pub input_loc: MemArea,
|
||||
pub return_loc: MemArea,
|
||||
}
|
||||
|
||||
impl GetTagExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
reason_strat(),
|
||||
HostMemory::mem_area_strat(types::Reason::guest_size()),
|
||||
HostMemory::mem_area_strat(types::Excuse::guest_size()),
|
||||
)
|
||||
.prop_map(|(input, input_loc, return_loc)| GetTagExercise {
|
||||
input,
|
||||
input_loc,
|
||||
return_loc,
|
||||
})
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.input_loc, e.return_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let discriminant: u8 = reason_tag(&self.input).into();
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr)
|
||||
.write(discriminant)
|
||||
.expect("input discriminant ptr");
|
||||
match self.input {
|
||||
types::Reason::DogAte(f) => {
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr + 4)
|
||||
.write(f)
|
||||
.expect("input contents ref_mut");
|
||||
}
|
||||
types::Reason::Traffic(v) => host_memory
|
||||
.ptr(self.input_loc.ptr + 4)
|
||||
.write(v)
|
||||
.expect("input contents ref_mut"),
|
||||
types::Reason::Sleeping => {} // Do nothing
|
||||
}
|
||||
let e = union_example::get_tag(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.return_loc.ptr as i32,
|
||||
);
|
||||
|
||||
assert_eq!(e, types::Errno::Ok.into(), "get_tag errno");
|
||||
|
||||
let return_val: types::Excuse = host_memory
|
||||
.ptr(self.return_loc.ptr)
|
||||
.read()
|
||||
.expect("return ref");
|
||||
|
||||
assert_eq!(return_val, reason_tag(&self.input), "get_tag return value");
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn get_tag(e in GetTagExercise::strat()) {
|
||||
e.test();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReasonMultExercise {
|
||||
pub input: types::Reason,
|
||||
pub input_loc: MemArea,
|
||||
pub input_pointee_loc: MemArea,
|
||||
pub multiply_by: u32,
|
||||
}
|
||||
|
||||
impl ReasonMultExercise {
|
||||
pub fn strat() -> BoxedStrategy<Self> {
|
||||
(
|
||||
reason_strat(),
|
||||
HostMemory::mem_area_strat(types::Reason::guest_size()),
|
||||
HostMemory::mem_area_strat(4),
|
||||
prop::num::u32::ANY,
|
||||
)
|
||||
.prop_map(
|
||||
|(input, input_loc, input_pointee_loc, multiply_by)| ReasonMultExercise {
|
||||
input,
|
||||
input_loc,
|
||||
input_pointee_loc,
|
||||
multiply_by,
|
||||
},
|
||||
)
|
||||
.prop_filter("non-overlapping pointers", |e| {
|
||||
MemArea::non_overlapping_set(&[e.input_loc, e.input_pointee_loc])
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn test(&self) {
|
||||
let ctx = WasiCtx::new();
|
||||
let host_memory = HostMemory::new();
|
||||
|
||||
let discriminant: u8 = reason_tag(&self.input).into();
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr)
|
||||
.write(discriminant)
|
||||
.expect("input discriminant ref_mut");
|
||||
host_memory
|
||||
.ptr(self.input_loc.ptr + 4)
|
||||
.write(self.input_pointee_loc.ptr)
|
||||
.expect("input pointer ref_mut");
|
||||
|
||||
match self.input {
|
||||
types::Reason::DogAte(f) => {
|
||||
host_memory
|
||||
.ptr(self.input_pointee_loc.ptr)
|
||||
.write(f)
|
||||
.expect("input contents ref_mut");
|
||||
}
|
||||
types::Reason::Traffic(v) => {
|
||||
host_memory
|
||||
.ptr(self.input_pointee_loc.ptr)
|
||||
.write(v)
|
||||
.expect("input contents ref_mut");
|
||||
}
|
||||
types::Reason::Sleeping => {} // Do nothing
|
||||
}
|
||||
let e = union_example::reason_mult(
|
||||
&ctx,
|
||||
&host_memory,
|
||||
self.input_loc.ptr as i32,
|
||||
self.multiply_by as i32,
|
||||
);
|
||||
|
||||
assert_eq!(e, types::Errno::Ok.into(), "reason_mult errno");
|
||||
|
||||
match self.input {
|
||||
types::Reason::DogAte(f) => {
|
||||
let f_result: f32 = host_memory
|
||||
.ptr(self.input_pointee_loc.ptr)
|
||||
.read()
|
||||
.expect("input contents ref_mut");
|
||||
assert_eq!(
|
||||
mult_zero_nan(f, self.multiply_by),
|
||||
f_result,
|
||||
"DogAte result"
|
||||
)
|
||||
}
|
||||
types::Reason::Traffic(v) => {
|
||||
let v_result: i32 = host_memory
|
||||
.ptr(self.input_pointee_loc.ptr)
|
||||
.read()
|
||||
.expect("input contents ref_mut");
|
||||
assert_eq!(
|
||||
mult_lose_overflow(v, self.multiply_by),
|
||||
v_result,
|
||||
"Traffic result"
|
||||
)
|
||||
}
|
||||
types::Reason::Sleeping => {} // Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn reason_mult(e in ReasonMultExercise::strat()) {
|
||||
e.test();
|
||||
}
|
||||
}
|
||||
31
crates/wiggle/tests/union.witx
Normal file
31
crates/wiggle/tests/union.witx
Normal file
@@ -0,0 +1,31 @@
|
||||
(use "errno.witx")
|
||||
(use "excuse.witx")
|
||||
|
||||
;; Every worker needs a union. Organize your workplace!
|
||||
;; Fight for the full product of your labor!
|
||||
|
||||
(typename $reason
|
||||
(union $excuse
|
||||
(field $dog_ate f32)
|
||||
(field $traffic s32)
|
||||
(empty $sleeping)))
|
||||
|
||||
(typename $reason_mut
|
||||
(union $excuse
|
||||
(field $dog_ate (@witx pointer f32))
|
||||
(field $traffic (@witx pointer s32))
|
||||
(empty $sleeping)))
|
||||
|
||||
(module $union_example
|
||||
(@interface func (export "get_tag")
|
||||
(param $r $reason)
|
||||
(result $error $errno)
|
||||
(result $t $excuse)
|
||||
)
|
||||
|
||||
(@interface func (export "reason_mult")
|
||||
(param $r $reason_mut)
|
||||
(param $multiply_by u32)
|
||||
(result $error $errno)
|
||||
)
|
||||
)
|
||||
342
crates/wiggle/tests/wasi.rs
Normal file
342
crates/wiggle/tests/wasi.rs
Normal file
@@ -0,0 +1,342 @@
|
||||
use wiggle_runtime::{GuestBorrows, GuestError, GuestErrorType, GuestPtr};
|
||||
use wiggle_test::WasiCtx;
|
||||
|
||||
wiggle::from_witx!({
|
||||
witx: ["tests/wasi.witx"],
|
||||
ctx: WasiCtx,
|
||||
});
|
||||
|
||||
type Result<T> = std::result::Result<T, types::Errno>;
|
||||
|
||||
impl<'a> GuestErrorType<'a> for types::Errno {
|
||||
type Context = WasiCtx<'a>;
|
||||
|
||||
fn success() -> types::Errno {
|
||||
types::Errno::Success
|
||||
}
|
||||
|
||||
fn from_error(e: GuestError, ctx: &Self::Context) -> types::Errno {
|
||||
eprintln!("GUEST ERROR: {:?}", e);
|
||||
ctx.guest_errors.borrow_mut().push(e);
|
||||
types::Errno::Io
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
|
||||
fn args_get(&self, _argv: GuestPtr<GuestPtr<u8>>, _argv_buf: GuestPtr<u8>) -> Result<()> {
|
||||
unimplemented!("args_get")
|
||||
}
|
||||
|
||||
fn args_sizes_get(&self) -> Result<(types::Size, types::Size)> {
|
||||
unimplemented!("args_sizes_get")
|
||||
}
|
||||
|
||||
fn environ_get(
|
||||
&self,
|
||||
_environ: GuestPtr<GuestPtr<u8>>,
|
||||
_environ_buf: GuestPtr<u8>,
|
||||
) -> Result<()> {
|
||||
unimplemented!("environ_get")
|
||||
}
|
||||
|
||||
fn environ_sizes_get(&self) -> Result<(types::Size, types::Size)> {
|
||||
unimplemented!("environ_sizes_get")
|
||||
}
|
||||
|
||||
fn clock_res_get(&self, _id: types::Clockid) -> Result<types::Timestamp> {
|
||||
unimplemented!("clock_res_get")
|
||||
}
|
||||
|
||||
fn clock_time_get(
|
||||
&self,
|
||||
_id: types::Clockid,
|
||||
_precision: types::Timestamp,
|
||||
) -> Result<types::Timestamp> {
|
||||
unimplemented!("clock_time_get")
|
||||
}
|
||||
|
||||
fn fd_advise(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filesize,
|
||||
_len: types::Filesize,
|
||||
_advice: types::Advice,
|
||||
) -> Result<()> {
|
||||
unimplemented!("fd_advise")
|
||||
}
|
||||
|
||||
fn fd_allocate(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filesize,
|
||||
_len: types::Filesize,
|
||||
) -> Result<()> {
|
||||
unimplemented!("fd_allocate")
|
||||
}
|
||||
|
||||
fn fd_close(&self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_close")
|
||||
}
|
||||
|
||||
fn fd_datasync(&self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_datasync")
|
||||
}
|
||||
|
||||
fn fd_fdstat_get(&self, _fd: types::Fd) -> Result<types::Fdstat> {
|
||||
unimplemented!("fd_fdstat_get")
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_flags(&self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> {
|
||||
unimplemented!("fd_fdstat_set_flags")
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_rights(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_fs_rights_base: types::Rights,
|
||||
_fs_rights_inherting: types::Rights,
|
||||
) -> Result<()> {
|
||||
unimplemented!("fd_fdstat_set_rights")
|
||||
}
|
||||
|
||||
fn fd_filestat_get(&self, _fd: types::Fd) -> Result<types::Filestat> {
|
||||
unimplemented!("fd_filestat_get")
|
||||
}
|
||||
|
||||
fn fd_filestat_set_size(&self, _fd: types::Fd, _size: types::Filesize) -> Result<()> {
|
||||
unimplemented!("fd_filestat_set_size")
|
||||
}
|
||||
|
||||
fn fd_filestat_set_times(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_atim: types::Timestamp,
|
||||
_mtim: types::Timestamp,
|
||||
_fst_flags: types::Fstflags,
|
||||
) -> Result<()> {
|
||||
unimplemented!("fd_filestat_set_times")
|
||||
}
|
||||
|
||||
fn fd_pread(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
iovs: &types::IovecArray<'_>,
|
||||
_offset: types::Filesize,
|
||||
) -> Result<types::Size> {
|
||||
// This is not functional code, but the type annotations demonstrate
|
||||
// that we can use the wiggle API to create the datastructures we want
|
||||
// for efficient implementation of this function elsewhere.
|
||||
|
||||
let mut bc = GuestBorrows::new();
|
||||
let mut slices: Vec<&'_ mut [u8]> = Vec::new();
|
||||
// Mark the iov elements as borrowed, to ensure that they does not
|
||||
// overlap with any of the as_raw regions.
|
||||
bc.borrow_slice(&iovs).expect("borrow iovec array");
|
||||
for iov_ptr in iovs.iter() {
|
||||
let iov_ptr = iov_ptr.expect("iovec element pointer is valid");
|
||||
|
||||
let iov: types::Iovec = iov_ptr.read().expect("read iovec element");
|
||||
let base: GuestPtr<u8> = iov.buf;
|
||||
let len: u32 = iov.buf_len;
|
||||
let buf: GuestPtr<[u8]> = base.as_array(len);
|
||||
let slice = buf.as_raw(&mut bc).expect("borrow slice from iovec");
|
||||
slices.push(unsafe { &mut *slice });
|
||||
}
|
||||
println!("iovec slices: {:?}", slices);
|
||||
unimplemented!("fd_pread")
|
||||
}
|
||||
|
||||
fn fd_prestat_get(&self, _fd: types::Fd) -> Result<types::Prestat> {
|
||||
unimplemented!("fd_prestat_get")
|
||||
}
|
||||
|
||||
fn fd_prestat_dir_name(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_path: GuestPtr<u8>,
|
||||
_path_len: types::Size,
|
||||
) -> Result<()> {
|
||||
unimplemented!("fd_prestat_dir_name")
|
||||
}
|
||||
|
||||
fn fd_pwrite(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_ciovs: &types::CiovecArray<'_>,
|
||||
_offset: types::Filesize,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("fd_pwrite")
|
||||
}
|
||||
|
||||
fn fd_read(&self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result<types::Size> {
|
||||
unimplemented!("fd_read")
|
||||
}
|
||||
|
||||
fn fd_readdir(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_buf: GuestPtr<u8>,
|
||||
_buf_len: types::Size,
|
||||
_cookie: types::Dircookie,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("fd_readdir")
|
||||
}
|
||||
|
||||
fn fd_renumber(&self, _fd: types::Fd, _to: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_renumber")
|
||||
}
|
||||
|
||||
fn fd_seek(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_offset: types::Filedelta,
|
||||
_whence: types::Whence,
|
||||
) -> Result<types::Filesize> {
|
||||
unimplemented!("fd_seek")
|
||||
}
|
||||
|
||||
fn fd_sync(&self, _fd: types::Fd) -> Result<()> {
|
||||
unimplemented!("fd_sync")
|
||||
}
|
||||
|
||||
fn fd_tell(&self, _fd: types::Fd) -> Result<types::Filesize> {
|
||||
unimplemented!("fd_tell")
|
||||
}
|
||||
|
||||
fn fd_write(&self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result<types::Size> {
|
||||
unimplemented!("fd_write")
|
||||
}
|
||||
|
||||
fn path_create_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_create_directory")
|
||||
}
|
||||
|
||||
fn path_filestat_get(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_flags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
) -> Result<types::Filestat> {
|
||||
unimplemented!("path_filestat_get")
|
||||
}
|
||||
|
||||
fn path_filestat_set_times(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_flags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
_atim: types::Timestamp,
|
||||
_mtim: types::Timestamp,
|
||||
_fst_flags: types::Fstflags,
|
||||
) -> Result<()> {
|
||||
unimplemented!("path_filestat_set_times")
|
||||
}
|
||||
|
||||
fn path_link(
|
||||
&self,
|
||||
_old_fd: types::Fd,
|
||||
_old_flags: types::Lookupflags,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
_new_fd: types::Fd,
|
||||
_new_path: &GuestPtr<'_, str>,
|
||||
) -> Result<()> {
|
||||
unimplemented!("path_link")
|
||||
}
|
||||
|
||||
fn path_open(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_dirflags: types::Lookupflags,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
_oflags: types::Oflags,
|
||||
_fs_rights_base: types::Rights,
|
||||
_fs_rights_inherting: types::Rights,
|
||||
_fdflags: types::Fdflags,
|
||||
) -> Result<types::Fd> {
|
||||
unimplemented!("path_open")
|
||||
}
|
||||
|
||||
fn path_readlink(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_path: &GuestPtr<'_, str>,
|
||||
_buf: GuestPtr<u8>,
|
||||
_buf_len: types::Size,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("path_readlink")
|
||||
}
|
||||
|
||||
fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_remove_directory")
|
||||
}
|
||||
|
||||
fn path_rename(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
_new_fd: types::Fd,
|
||||
_new_path: &GuestPtr<'_, str>,
|
||||
) -> Result<()> {
|
||||
unimplemented!("path_rename")
|
||||
}
|
||||
|
||||
fn path_symlink(
|
||||
&self,
|
||||
_old_path: &GuestPtr<'_, str>,
|
||||
_fd: types::Fd,
|
||||
_new_path: &GuestPtr<'_, str>,
|
||||
) -> Result<()> {
|
||||
unimplemented!("path_symlink")
|
||||
}
|
||||
|
||||
fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||
unimplemented!("path_unlink_file")
|
||||
}
|
||||
|
||||
fn poll_oneoff(
|
||||
&self,
|
||||
_in_: GuestPtr<types::Subscription>,
|
||||
_out: GuestPtr<types::Event>,
|
||||
_nsubscriptions: types::Size,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("poll_oneoff")
|
||||
}
|
||||
|
||||
fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> {
|
||||
unimplemented!("proc_exit")
|
||||
}
|
||||
|
||||
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {
|
||||
unimplemented!("proc_raise")
|
||||
}
|
||||
|
||||
fn sched_yield(&self) -> Result<()> {
|
||||
unimplemented!("sched_yield")
|
||||
}
|
||||
|
||||
fn random_get(&self, _buf: GuestPtr<u8>, _buf_len: types::Size) -> Result<()> {
|
||||
unimplemented!("random_get")
|
||||
}
|
||||
|
||||
fn sock_recv(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_ri_data: &types::IovecArray<'_>,
|
||||
_ri_flags: types::Riflags,
|
||||
) -> Result<(types::Size, types::Roflags)> {
|
||||
unimplemented!("sock_recv")
|
||||
}
|
||||
|
||||
fn sock_send(
|
||||
&self,
|
||||
_fd: types::Fd,
|
||||
_si_data: &types::CiovecArray<'_>,
|
||||
_si_flags: types::Siflags,
|
||||
) -> Result<types::Size> {
|
||||
unimplemented!("sock_send")
|
||||
}
|
||||
|
||||
fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> {
|
||||
unimplemented!("sock_shutdown")
|
||||
}
|
||||
}
|
||||
532
crates/wiggle/tests/wasi.witx
Normal file
532
crates/wiggle/tests/wasi.witx
Normal file
@@ -0,0 +1,532 @@
|
||||
;; WASI Preview. This is an evolution of the API that WASI initially
|
||||
;; launched with.
|
||||
;;
|
||||
;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi).
|
||||
;;
|
||||
;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/master/docs/witx.md)
|
||||
;; for an explanation of what that means.
|
||||
|
||||
(use "typenames.witx")
|
||||
|
||||
(module $wasi_snapshot_preview1
|
||||
;;; Linear memory to be accessed by WASI functions that need it.
|
||||
(import "memory" (memory))
|
||||
|
||||
;;; Read command-line argument data.
|
||||
;;; The size of the array should match that returned by `args_sizes_get`
|
||||
(@interface func (export "args_get")
|
||||
(param $argv (@witx pointer (@witx pointer u8)))
|
||||
(param $argv_buf (@witx pointer u8))
|
||||
(result $error $errno)
|
||||
)
|
||||
;;; Return command-line argument data sizes.
|
||||
(@interface func (export "args_sizes_get")
|
||||
(result $error $errno)
|
||||
;;; The number of arguments.
|
||||
(result $argc $size)
|
||||
;;; The size of the argument string data.
|
||||
(result $argv_buf_size $size)
|
||||
)
|
||||
|
||||
;;; Read environment variable data.
|
||||
;;; The sizes of the buffers should match that returned by `environ_sizes_get`.
|
||||
(@interface func (export "environ_get")
|
||||
(param $environ (@witx pointer (@witx pointer u8)))
|
||||
(param $environ_buf (@witx pointer u8))
|
||||
(result $error $errno)
|
||||
)
|
||||
;;; Return environment variable data sizes.
|
||||
(@interface func (export "environ_sizes_get")
|
||||
(result $error $errno)
|
||||
;;; The number of environment variable arguments.
|
||||
(result $environc $size)
|
||||
;;; The size of the environment variable data.
|
||||
(result $environ_buf_size $size)
|
||||
)
|
||||
|
||||
;;; Return the resolution of a clock.
|
||||
;;; Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
|
||||
;;; return `errno::inval`.
|
||||
;;; Note: This is similar to `clock_getres` in POSIX.
|
||||
(@interface func (export "clock_res_get")
|
||||
;;; The clock for which to return the resolution.
|
||||
(param $id $clockid)
|
||||
(result $error $errno)
|
||||
;;; The resolution of the clock.
|
||||
(result $resolution $timestamp)
|
||||
)
|
||||
;;; Return the time value of a clock.
|
||||
;;; Note: This is similar to `clock_gettime` in POSIX.
|
||||
(@interface func (export "clock_time_get")
|
||||
;;; The clock for which to return the time.
|
||||
(param $id $clockid)
|
||||
;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
|
||||
(param $precision $timestamp)
|
||||
(result $error $errno)
|
||||
;;; The time value of the clock.
|
||||
(result $time $timestamp)
|
||||
)
|
||||
|
||||
;;; Provide file advisory information on a file descriptor.
|
||||
;;; Note: This is similar to `posix_fadvise` in POSIX.
|
||||
(@interface func (export "fd_advise")
|
||||
(param $fd $fd)
|
||||
;;; The offset within the file to which the advisory applies.
|
||||
(param $offset $filesize)
|
||||
;;; The length of the region to which the advisory applies.
|
||||
(param $len $filesize)
|
||||
;;; The advice.
|
||||
(param $advice $advice)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Force the allocation of space in a file.
|
||||
;;; Note: This is similar to `posix_fallocate` in POSIX.
|
||||
(@interface func (export "fd_allocate")
|
||||
(param $fd $fd)
|
||||
;;; The offset at which to start the allocation.
|
||||
(param $offset $filesize)
|
||||
;;; The length of the area that is allocated.
|
||||
(param $len $filesize)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Close a file descriptor.
|
||||
;;; Note: This is similar to `close` in POSIX.
|
||||
(@interface func (export "fd_close")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Synchronize the data of a file to disk.
|
||||
;;; Note: This is similar to `fdatasync` in POSIX.
|
||||
(@interface func (export "fd_datasync")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Get the attributes of a file descriptor.
|
||||
;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
|
||||
(@interface func (export "fd_fdstat_get")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
;;; The buffer where the file descriptor's attributes are stored.
|
||||
(result $stat $fdstat)
|
||||
)
|
||||
|
||||
;;; Adjust the flags associated with a file descriptor.
|
||||
;;; Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
|
||||
(@interface func (export "fd_fdstat_set_flags")
|
||||
(param $fd $fd)
|
||||
;;; The desired values of the file descriptor flags.
|
||||
(param $flags $fdflags)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Adjust the rights associated with a file descriptor.
|
||||
;;; This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights
|
||||
(@interface func (export "fd_fdstat_set_rights")
|
||||
(param $fd $fd)
|
||||
;;; The desired rights of the file descriptor.
|
||||
(param $fs_rights_base $rights)
|
||||
(param $fs_rights_inheriting $rights)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Return the attributes of an open file.
|
||||
(@interface func (export "fd_filestat_get")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
;;; The buffer where the file's attributes are stored.
|
||||
(result $buf $filestat)
|
||||
)
|
||||
|
||||
;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
|
||||
;;; Note: This is similar to `ftruncate` in POSIX.
|
||||
(@interface func (export "fd_filestat_set_size")
|
||||
(param $fd $fd)
|
||||
;;; The desired file size.
|
||||
(param $size $filesize)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Adjust the timestamps of an open file or directory.
|
||||
;;; Note: This is similar to `futimens` in POSIX.
|
||||
(@interface func (export "fd_filestat_set_times")
|
||||
(param $fd $fd)
|
||||
;;; The desired values of the data access timestamp.
|
||||
(param $atim $timestamp)
|
||||
;;; The desired values of the data modification timestamp.
|
||||
(param $mtim $timestamp)
|
||||
;;; A bitmask indicating which timestamps to adjust.
|
||||
(param $fst_flags $fstflags)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Read from a file descriptor, without using and updating the file descriptor's offset.
|
||||
;;; Note: This is similar to `preadv` in POSIX.
|
||||
(@interface func (export "fd_pread")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors in which to store data.
|
||||
(param $iovs $iovec_array)
|
||||
;;; The offset within the file at which to read.
|
||||
(param $offset $filesize)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes read.
|
||||
(result $nread $size)
|
||||
)
|
||||
|
||||
;;; Return a description of the given preopened file descriptor.
|
||||
(@interface func (export "fd_prestat_get")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
;;; The buffer where the description is stored.
|
||||
(result $buf $prestat)
|
||||
)
|
||||
|
||||
;;; Return a description of the given preopened file descriptor.
|
||||
(@interface func (export "fd_prestat_dir_name")
|
||||
(param $fd $fd)
|
||||
;;; A buffer into which to write the preopened directory name.
|
||||
(param $path (@witx pointer u8))
|
||||
(param $path_len $size)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Write to a file descriptor, without using and updating the file descriptor's offset.
|
||||
;;; Note: This is similar to `pwritev` in POSIX.
|
||||
(@interface func (export "fd_pwrite")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors from which to retrieve data.
|
||||
(param $iovs $ciovec_array)
|
||||
;;; The offset within the file at which to write.
|
||||
(param $offset $filesize)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes written.
|
||||
(result $nwritten $size)
|
||||
)
|
||||
|
||||
;;; Read from a file descriptor.
|
||||
;;; Note: This is similar to `readv` in POSIX.
|
||||
(@interface func (export "fd_read")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors to which to store data.
|
||||
(param $iovs $iovec_array)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes read.
|
||||
(result $nread $size)
|
||||
)
|
||||
|
||||
;;; Read directory entries from a directory.
|
||||
;;; When successful, the contents of the output buffer consist of a sequence of
|
||||
;;; directory entries. Each directory entry consists of a dirent_t object,
|
||||
;;; followed by dirent_t::d_namlen bytes holding the name of the directory
|
||||
;;; entry.
|
||||
;;
|
||||
;;; This function fills the output buffer as much as possible, potentially
|
||||
;;; truncating the last directory entry. This allows the caller to grow its
|
||||
;;; read buffer size in case it's too small to fit a single large directory
|
||||
;;; entry, or skip the oversized directory entry.
|
||||
(@interface func (export "fd_readdir")
|
||||
(param $fd $fd)
|
||||
;;; The buffer where directory entries are stored
|
||||
(param $buf (@witx pointer u8))
|
||||
(param $buf_len $size)
|
||||
;;; The location within the directory to start reading
|
||||
(param $cookie $dircookie)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached.
|
||||
(result $bufused $size)
|
||||
)
|
||||
|
||||
;;; Atomically replace a file descriptor by renumbering another file descriptor.
|
||||
;;
|
||||
;;; Due to the strong focus on thread safety, this environment does not provide
|
||||
;;; a mechanism to duplicate or renumber a file descriptor to an arbitrary
|
||||
;;; number, like `dup2()`. This would be prone to race conditions, as an actual
|
||||
;;; file descriptor with the same number could be allocated by a different
|
||||
;;; thread at the same time.
|
||||
;;
|
||||
;;; This function provides a way to atomically renumber file descriptors, which
|
||||
;;; would disappear if `dup2()` were to be removed entirely.
|
||||
(@interface func (export "fd_renumber")
|
||||
(param $fd $fd)
|
||||
;;; The file descriptor to overwrite.
|
||||
(param $to $fd)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Move the offset of a file descriptor.
|
||||
;;; Note: This is similar to `lseek` in POSIX.
|
||||
(@interface func (export "fd_seek")
|
||||
(param $fd $fd)
|
||||
;;; The number of bytes to move.
|
||||
(param $offset $filedelta)
|
||||
;;; The base from which the offset is relative.
|
||||
(param $whence $whence)
|
||||
(result $error $errno)
|
||||
;;; The new offset of the file descriptor, relative to the start of the file.
|
||||
(result $newoffset $filesize)
|
||||
)
|
||||
|
||||
;;; Synchronize the data and metadata of a file to disk.
|
||||
;;; Note: This is similar to `fsync` in POSIX.
|
||||
(@interface func (export "fd_sync")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Return the current offset of a file descriptor.
|
||||
;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
|
||||
(@interface func (export "fd_tell")
|
||||
(param $fd $fd)
|
||||
(result $error $errno)
|
||||
;;; The current offset of the file descriptor, relative to the start of the file.
|
||||
(result $offset $filesize)
|
||||
)
|
||||
|
||||
;;; Write to a file descriptor.
|
||||
;;; Note: This is similar to `writev` in POSIX.
|
||||
(@interface func (export "fd_write")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors from which to retrieve data.
|
||||
(param $iovs $ciovec_array)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes written.
|
||||
(result $nwritten $size)
|
||||
)
|
||||
|
||||
;;; Create a directory.
|
||||
;;; Note: This is similar to `mkdirat` in POSIX.
|
||||
(@interface func (export "path_create_directory")
|
||||
(param $fd $fd)
|
||||
;;; The path at which to create the directory.
|
||||
(param $path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Return the attributes of a file or directory.
|
||||
;;; Note: This is similar to `stat` in POSIX.
|
||||
(@interface func (export "path_filestat_get")
|
||||
(param $fd $fd)
|
||||
;;; Flags determining the method of how the path is resolved.
|
||||
(param $flags $lookupflags)
|
||||
;;; The path of the file or directory to inspect.
|
||||
(param $path string)
|
||||
(result $error $errno)
|
||||
;;; The buffer where the file's attributes are stored.
|
||||
(result $buf $filestat)
|
||||
)
|
||||
|
||||
;;; Adjust the timestamps of a file or directory.
|
||||
;;; Note: This is similar to `utimensat` in POSIX.
|
||||
(@interface func (export "path_filestat_set_times")
|
||||
(param $fd $fd)
|
||||
;;; Flags determining the method of how the path is resolved.
|
||||
(param $flags $lookupflags)
|
||||
;;; The path of the file or directory to operate on.
|
||||
(param $path string)
|
||||
;;; The desired values of the data access timestamp.
|
||||
(param $atim $timestamp)
|
||||
;;; The desired values of the data modification timestamp.
|
||||
(param $mtim $timestamp)
|
||||
;;; A bitmask indicating which timestamps to adjust.
|
||||
(param $fst_flags $fstflags)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Create a hard link.
|
||||
;;; Note: This is similar to `linkat` in POSIX.
|
||||
(@interface func (export "path_link")
|
||||
(param $old_fd $fd)
|
||||
;;; Flags determining the method of how the path is resolved.
|
||||
(param $old_flags $lookupflags)
|
||||
;;; The source path from which to link.
|
||||
(param $old_path string)
|
||||
;;; The working directory at which the resolution of the new path starts.
|
||||
(param $new_fd $fd)
|
||||
;;; The destination path at which to create the hard link.
|
||||
(param $new_path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Open a file or directory.
|
||||
;;
|
||||
;;; The returned file descriptor is not guaranteed to be the lowest-numbered
|
||||
;;; file descriptor not currently open; it is randomized to prevent
|
||||
;;; applications from depending on making assumptions about indexes, since this
|
||||
;;; is error-prone in multi-threaded contexts. The returned file descriptor is
|
||||
;;; guaranteed to be less than 2**31.
|
||||
;;
|
||||
;;; Note: This is similar to `openat` in POSIX.
|
||||
(@interface func (export "path_open")
|
||||
(param $fd $fd)
|
||||
;;; Flags determining the method of how the path is resolved.
|
||||
(param $dirflags $lookupflags)
|
||||
;;; The relative path of the file or directory to open, relative to the
|
||||
;;; `path_open::fd` directory.
|
||||
(param $path string)
|
||||
;;; The method by which to open the file.
|
||||
(param $oflags $oflags)
|
||||
;;; The initial rights of the newly created file descriptor. The
|
||||
;;; implementation is allowed to return a file descriptor with fewer rights
|
||||
;;; than specified, if and only if those rights do not apply to the type of
|
||||
;;; file being opened.
|
||||
;;
|
||||
;;; The *base* rights are rights that will apply to operations using the file
|
||||
;;; descriptor itself, while the *inheriting* rights are rights that apply to
|
||||
;;; file descriptors derived from it.
|
||||
(param $fs_rights_base $rights)
|
||||
(param $fs_rights_inherting $rights)
|
||||
(param $fdflags $fdflags)
|
||||
(result $error $errno)
|
||||
;;; The file descriptor of the file that has been opened.
|
||||
(result $opened_fd $fd)
|
||||
)
|
||||
|
||||
;;; Read the contents of a symbolic link.
|
||||
;;; Note: This is similar to `readlinkat` in POSIX.
|
||||
(@interface func (export "path_readlink")
|
||||
(param $fd $fd)
|
||||
;;; The path of the symbolic link from which to read.
|
||||
(param $path string)
|
||||
;;; The buffer to which to write the contents of the symbolic link.
|
||||
(param $buf (@witx pointer u8))
|
||||
(param $buf_len $size)
|
||||
(result $error $errno)
|
||||
;;; The number of bytes placed in the buffer.
|
||||
(result $bufused $size)
|
||||
)
|
||||
|
||||
;;; Remove a directory.
|
||||
;;; Return `errno::notempty` if the directory is not empty.
|
||||
;;; Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
|
||||
(@interface func (export "path_remove_directory")
|
||||
(param $fd $fd)
|
||||
;;; The path to a directory to remove.
|
||||
(param $path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Rename a file or directory.
|
||||
;;; Note: This is similar to `renameat` in POSIX.
|
||||
(@interface func (export "path_rename")
|
||||
(param $fd $fd)
|
||||
;;; The source path of the file or directory to rename.
|
||||
(param $old_path string)
|
||||
;;; The working directory at which the resolution of the new path starts.
|
||||
(param $new_fd $fd)
|
||||
;;; The destination path to which to rename the file or directory.
|
||||
(param $new_path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Create a symbolic link.
|
||||
;;; Note: This is similar to `symlinkat` in POSIX.
|
||||
(@interface func (export "path_symlink")
|
||||
;;; The contents of the symbolic link.
|
||||
(param $old_path string)
|
||||
(param $fd $fd)
|
||||
;;; The destination path at which to create the symbolic link.
|
||||
(param $new_path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
|
||||
;;; Unlink a file.
|
||||
;;; Return `errno::isdir` if the path refers to a directory.
|
||||
;;; Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
|
||||
(@interface func (export "path_unlink_file")
|
||||
(param $fd $fd)
|
||||
;;; The path to a file to unlink.
|
||||
(param $path string)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Concurrently poll for the occurrence of a set of events.
|
||||
(@interface func (export "poll_oneoff")
|
||||
;;; The events to which to subscribe.
|
||||
(param $in (@witx const_pointer $subscription))
|
||||
;;; The events that have occurred.
|
||||
(param $out (@witx pointer $event))
|
||||
;;; Both the number of subscriptions and events.
|
||||
(param $nsubscriptions $size)
|
||||
(result $error $errno)
|
||||
;;; The number of events stored.
|
||||
(result $nevents $size)
|
||||
)
|
||||
|
||||
;;; Terminate the process normally. An exit code of 0 indicates successful
|
||||
;;; termination of the program. The meanings of other values is dependent on
|
||||
;;; the environment.
|
||||
(@interface func (export "proc_exit")
|
||||
;;; The exit code returned by the process.
|
||||
(param $rval $exitcode)
|
||||
)
|
||||
|
||||
;;; Send a signal to the process of the calling thread.
|
||||
;;; Note: This is similar to `raise` in POSIX.
|
||||
(@interface func (export "proc_raise")
|
||||
;;; The signal condition to trigger.
|
||||
(param $sig $signal)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Temporarily yield execution of the calling thread.
|
||||
;;; Note: This is similar to `sched_yield` in POSIX.
|
||||
(@interface func (export "sched_yield")
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Write high-quality random data into a buffer.
|
||||
;;; This function blocks when the implementation is unable to immediately
|
||||
;;; provide sufficient high-quality random data.
|
||||
;;; This function may execute slowly, so when large mounts of random data are
|
||||
;;; required, it's advisable to use this function to seed a pseudo-random
|
||||
;;; number generator, rather than to provide the random data directly.
|
||||
(@interface func (export "random_get")
|
||||
;;; The buffer to fill with random data.
|
||||
(param $buf (@witx pointer u8))
|
||||
(param $buf_len $size)
|
||||
(result $error $errno)
|
||||
)
|
||||
|
||||
;;; Receive a message from a socket.
|
||||
;;; Note: This is similar to `recv` in POSIX, though it also supports reading
|
||||
;;; the data into multiple buffers in the manner of `readv`.
|
||||
(@interface func (export "sock_recv")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors to which to store data.
|
||||
(param $ri_data $iovec_array)
|
||||
;;; Message flags.
|
||||
(param $ri_flags $riflags)
|
||||
(result $error $errno)
|
||||
;;; Number of bytes stored in ri_data.
|
||||
(result $ro_datalen $size)
|
||||
;;; Message flags.
|
||||
(result $ro_flags $roflags)
|
||||
)
|
||||
|
||||
;;; Send a message on a socket.
|
||||
;;; Note: This is similar to `send` in POSIX, though it also supports writing
|
||||
;;; the data from multiple buffers in the manner of `writev`.
|
||||
(@interface func (export "sock_send")
|
||||
(param $fd $fd)
|
||||
;;; List of scatter/gather vectors to which to retrieve data
|
||||
(param $si_data $ciovec_array)
|
||||
;;; Message flags.
|
||||
(param $si_flags $siflags)
|
||||
(result $error $errno)
|
||||
;;; Number of bytes transmitted.
|
||||
(result $so_datalen $size)
|
||||
)
|
||||
|
||||
;;; Shut down socket send and receive channels.
|
||||
;;; Note: This is similar to `shutdown` in POSIX.
|
||||
(@interface func (export "sock_shutdown")
|
||||
(param $fd $fd)
|
||||
;;; Which channels on the socket to shut down.
|
||||
(param $how $sdflags)
|
||||
(result $error $errno)
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user