Rewrite for recursive safety
This commit rewrites the runtime crate to provide safety in the face of recursive calls to the guest. The basic principle is that `GuestMemory` is now a trait which dynamically returns the pointer/length pair. This also has an implicit contract (hence the `unsafe` trait) that the pointer/length pair point to a valid list of bytes in host memory "until something is reentrant". After this changes the various suite of `Guest*` types were rewritten. `GuestRef` and `GuestRefMut` were both removed since they cannot safely exist. The `GuestPtrMut` type was removed for simplicity, and the final `GuestPtr` type subsumes `GuestString` and `GuestArray`. This means that there's only one guest pointer type, `GuestPtr<'a, T>`, where `'a` is the borrow into host memory, basically borrowing the `GuestMemory` trait object itself. Some core utilities are exposed on `GuestPtr`, but they're all 100% safe. Unsafety is now entirely contained within a few small locations: * Implementations of the `GuestType` for primitive types (e.g. `i8`, `u8`, etc) use `unsafe` to read/write memory. The `unsafe` trait of `GuestMemory` though should prove that they're safe. * `GuestPtr<'_, str>` has a method which validates utf-8 contents, and this requires `unsafe` internally to read all the bytes. This is guaranteed to be safe however given the contract of `GuestMemory`. And that's it! Everything else is a bunch of safe combinators all built up on the various utilities provided by `GuestPtr`. The general idioms are roughly the same as before, with various tweaks here and there. A summary of expected idioms are: * For small values you'd `.read()` or `.write()` very quickly. You'd pass around the type itself. * For strings, you'd pass `GuestPtr<'_, str>` down to the point where it's actually consumed. At that moment you'd either decide to copy it out (a safe operation) or you'd get a raw view to the string (an unsafe operation) and assert that you won't call back into wasm while you're holding that pointer. * Arrays are similar to strings, passing around `GuestPtr<'_, [T]>`. Arrays also have a `iter()` method which yields an iterator of `GuestPtr<'_, T>` for convenience. Overall there's still a lot of missing documentation on the runtime crate specifically around the safety of the `GuestMemory` trait as well as how the utilities/methods are expected to be used. Additionally there's utilities which aren't currently implemented which would be easy to implement. For example there's no method to copy out a string or a slice, although that would be pretty easy to add. In any case I'm curious to get feedback on this approach and see what y'all think!
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use crate::lifetimes::{anon_lifetime, LifetimeExt};
|
use crate::lifetimes::anon_lifetime;
|
||||||
use crate::names::Names;
|
use crate::names::Names;
|
||||||
|
|
||||||
pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
|
pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
|
||||||
@@ -30,7 +30,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let abi_args = quote!(
|
let abi_args = quote!(
|
||||||
ctx: &mut #ctx_type, memory: &mut wiggle_runtime::GuestMemory,
|
ctx: &#ctx_type, memory: &dyn wiggle_runtime::GuestMemory,
|
||||||
#(#params),*
|
#(#params),*
|
||||||
);
|
);
|
||||||
let abi_ret = if let Some(ret) = &coretype.ret {
|
let abi_ret = if let Some(ret) = &coretype.ret {
|
||||||
@@ -158,13 +158,8 @@ fn marshal_arg(
|
|||||||
let arg_name = names.func_ptr_binding(¶m.name);
|
let arg_name = names.func_ptr_binding(¶m.name);
|
||||||
let name = names.func_param(¶m.name);
|
let name = names.func_param(¶m.name);
|
||||||
quote! {
|
quote! {
|
||||||
let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) {
|
let #name = match wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #arg_name as u32).read() {
|
||||||
Ok(p) => match p.read() {
|
Ok(r) => r,
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
#error_handling
|
#error_handling
|
||||||
}
|
}
|
||||||
@@ -209,102 +204,25 @@ fn marshal_arg(
|
|||||||
let len_name = names.func_len_binding(¶m.name);
|
let len_name = names.func_len_binding(¶m.name);
|
||||||
let name = names.func_param(¶m.name);
|
let name = names.func_param(¶m.name);
|
||||||
quote! {
|
quote! {
|
||||||
let num_elems = match memory.ptr::<u32>(#len_name as u32) {
|
let #name = wiggle_runtime::GuestPtr::<#lifetime, str>::new(memory, (#ptr_name as u32, #len_name as u32));
|
||||||
Ok(p) => match p.as_ref() {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let #name: wiggle_runtime::GuestString<#lifetime> = match memory.ptr::<u8>(#ptr_name as u32) {
|
|
||||||
Ok(p) => match p.array(*num_elems) {
|
|
||||||
Ok(s) => s.into(),
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
witx::Type::Pointer(pointee) => {
|
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||||
let pointee_type = names.type_ref(pointee, anon_lifetime());
|
let pointee_type = names.type_ref(pointee, anon_lifetime());
|
||||||
let name = names.func_param(¶m.name);
|
let name = names.func_param(¶m.name);
|
||||||
quote! {
|
quote! {
|
||||||
let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) {
|
let #name = wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #name as u32);
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
witx::Type::ConstPointer(pointee) => {
|
witx::Type::Struct(_) => read_conversion,
|
||||||
let pointee_type = names.type_ref(pointee, anon_lifetime());
|
|
||||||
let name = names.func_param(¶m.name);
|
|
||||||
quote! {
|
|
||||||
let #name = match memory.ptr::<#pointee_type>(#name as u32) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
witx::Type::Struct(s) if !s.needs_lifetime() => {
|
|
||||||
let pointee_type = names.type_ref(tref, anon_lifetime());
|
|
||||||
let arg_name = names.func_ptr_binding(¶m.name);
|
|
||||||
let name = names.func_param(¶m.name);
|
|
||||||
quote! {
|
|
||||||
let #name = match memory.ptr::<#pointee_type>(#arg_name as u32) {
|
|
||||||
Ok(p) => match p.as_ref() {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
witx::Type::Struct(s) if s.needs_lifetime() => read_conversion,
|
|
||||||
witx::Type::Array(arr) => {
|
witx::Type::Array(arr) => {
|
||||||
let pointee_type = names.type_ref(arr, anon_lifetime());
|
let pointee_type = names.type_ref(arr, anon_lifetime());
|
||||||
let ptr_name = names.func_ptr_binding(¶m.name);
|
let ptr_name = names.func_ptr_binding(¶m.name);
|
||||||
let len_name = names.func_len_binding(¶m.name);
|
let len_name = names.func_len_binding(¶m.name);
|
||||||
let name = names.func_param(¶m.name);
|
let name = names.func_param(¶m.name);
|
||||||
quote! {
|
quote! {
|
||||||
let num_elems = match memory.ptr::<u32>(#len_name as u32) {
|
let #name = wiggle_runtime::GuestPtr::<[#pointee_type]>::new(memory, (#ptr_name as u32, #len_name as u32));
|
||||||
Ok(p) => match p.as_ref() {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let #name = match memory.ptr::<#pointee_type>(#ptr_name as u32) {
|
|
||||||
Ok(p) => match p.array(*num_elems) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
#error_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
witx::Type::Union(_u) => read_conversion,
|
witx::Type::Union(_u) => read_conversion,
|
||||||
@@ -313,7 +231,6 @@ fn marshal_arg(
|
|||||||
let handle_type = names.type_ref(tref, anon_lifetime());
|
let handle_type = names.type_ref(tref, anon_lifetime());
|
||||||
quote!( let #name = #handle_type::from(#name); )
|
quote!( let #name = #handle_type::from(#name); )
|
||||||
}
|
}
|
||||||
_ => unimplemented!("argument type marshalling"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,17 +250,14 @@ where
|
|||||||
let ptr_name = names.func_ptr_binding(&result.name);
|
let ptr_name = names.func_ptr_binding(&result.name);
|
||||||
let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str()));
|
let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str()));
|
||||||
let pre = quote! {
|
let pre = quote! {
|
||||||
let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) {
|
let #ptr_name = wiggle_runtime::GuestPtr::<#pointee_type>::new(memory, #ptr_name as u32);
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
#ptr_err_handling
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
// trait binding returns func_param name.
|
// trait binding returns func_param name.
|
||||||
let val_name = names.func_param(&result.name);
|
let val_name = names.func_param(&result.name);
|
||||||
let post = quote! {
|
let post = quote! {
|
||||||
#ptr_name.write(&#val_name);
|
if let Err(e) = #ptr_name.write(#val_name) {
|
||||||
|
#ptr_err_handling
|
||||||
|
}
|
||||||
};
|
};
|
||||||
(pre, post)
|
(pre, post)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ pub fn define_module_trait(names: &Names, m: &Module) -> TokenStream {
|
|||||||
// Check if we're returning an entity anotated with a lifetime,
|
// Check if we're returning an entity anotated with a lifetime,
|
||||||
// in which case, we'll need to annotate the function itself, and
|
// in which case, we'll need to annotate the function itself, and
|
||||||
// hence will need an explicit lifetime (rather than anonymous)
|
// hence will need an explicit lifetime (rather than anonymous)
|
||||||
let (lifetime, is_anonymous) = if f.results.iter().any(|ret| ret.tref.needs_lifetime()) {
|
let (lifetime, is_anonymous) = if f
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.chain(&f.results)
|
||||||
|
.any(|ret| ret.tref.needs_lifetime())
|
||||||
|
{
|
||||||
(quote!('a), false)
|
(quote!('a), false)
|
||||||
} else {
|
} else {
|
||||||
(anon_lifetime(), true)
|
(anon_lifetime(), true)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ impl Names {
|
|||||||
}
|
}
|
||||||
pub fn builtin_type(&self, b: BuiltinType, lifetime: TokenStream) -> TokenStream {
|
pub fn builtin_type(&self, b: BuiltinType, lifetime: TokenStream) -> TokenStream {
|
||||||
match b {
|
match b {
|
||||||
BuiltinType::String => quote!(wiggle_runtime::GuestString<#lifetime>),
|
BuiltinType::String => quote!(wiggle_runtime::GuestPtr<#lifetime, str>),
|
||||||
BuiltinType::U8 => quote!(u8),
|
BuiltinType::U8 => quote!(u8),
|
||||||
BuiltinType::U16 => quote!(u16),
|
BuiltinType::U16 => quote!(u16),
|
||||||
BuiltinType::U32 => quote!(u32),
|
BuiltinType::U32 => quote!(u32),
|
||||||
@@ -60,11 +60,7 @@ impl Names {
|
|||||||
}
|
}
|
||||||
TypeRef::Value(ty) => match &**ty {
|
TypeRef::Value(ty) => match &**ty {
|
||||||
witx::Type::Builtin(builtin) => self.builtin_type(*builtin, lifetime.clone()),
|
witx::Type::Builtin(builtin) => self.builtin_type(*builtin, lifetime.clone()),
|
||||||
witx::Type::Pointer(pointee) => {
|
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||||
let pointee_type = self.type_ref(&pointee, lifetime.clone());
|
|
||||||
quote!(wiggle_runtime::GuestPtrMut<#lifetime, #pointee_type>)
|
|
||||||
}
|
|
||||||
witx::Type::ConstPointer(pointee) => {
|
|
||||||
let pointee_type = self.type_ref(&pointee, lifetime.clone());
|
let pointee_type = self.type_ref(&pointee, lifetime.clone());
|
||||||
quote!(wiggle_runtime::GuestPtr<#lifetime, #pointee_type>)
|
quote!(wiggle_runtime::GuestPtr<#lifetime, #pointee_type>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,37 +77,25 @@ pub(super) fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||||
fn size() -> u32 {
|
fn guest_size() -> u32 {
|
||||||
::std::mem::size_of::<#repr>() as u32
|
#repr::guest_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align() -> u32 {
|
fn guest_align() -> usize {
|
||||||
::std::mem::align_of::<#repr>() as u32
|
#repr::guest_align()
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
use ::std::convert::TryFrom;
|
|
||||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
|
||||||
let _ = #ident::try_from(raw)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||||
// Perform validation as part of as_ref:
|
use std::convert::TryFrom;
|
||||||
let r = location.as_ref()?;
|
let val = #repr::read(&location.cast())?;
|
||||||
Ok(*r)
|
#ident::try_from(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self)
|
||||||
let val: #repr = #repr::from(*self);
|
-> Result<(), wiggle_runtime::GuestError>
|
||||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
{
|
||||||
|
#repr::write(&location.cast(), #repr::from(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,35 +126,24 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||||
fn size() -> u32 {
|
fn guest_size() -> u32 {
|
||||||
::std::mem::size_of::<#repr>() as u32
|
#repr::guest_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align() -> u32 {
|
fn guest_align() -> usize {
|
||||||
::std::mem::align_of::<#repr>() as u32
|
#repr::guest_align()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> String {
|
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||||
stringify!(#ident).to_owned()
|
use std::convert::TryFrom;
|
||||||
|
let bits = #repr::read(&location.cast())?;
|
||||||
|
#ident::try_from(bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> {
|
||||||
use ::std::convert::TryFrom;
|
let val: #repr = #repr::from(val);
|
||||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
#repr::write(&location.cast(), val)
|
||||||
let _ = #ident::try_from(raw)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
|
||||||
Ok(*location.as_ref()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
|
||||||
let val: #repr = #repr::from(*self);
|
|
||||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub(super) fn define_handle(
|
|||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let ident = names.type_(name);
|
let ident = names.type_(name);
|
||||||
let size = h.mem_size_align().size as u32;
|
let size = h.mem_size_align().size as u32;
|
||||||
let align = h.mem_size_align().align as u32;
|
let align = h.mem_size_align().align as usize;
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||||
pub struct #ident(u32);
|
pub struct #ident(u32);
|
||||||
@@ -46,32 +46,21 @@ pub(super) fn define_handle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||||
fn size() -> u32 {
|
fn guest_size() -> u32 {
|
||||||
#size
|
#size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align() -> u32 {
|
fn guest_align() -> usize {
|
||||||
#align
|
#align
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||||
let r = location.as_ref()?;
|
Ok(#ident(u32::read(&location.cast())?))
|
||||||
Ok(*r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle_runtime::GuestError> {
|
||||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
u32::write(&location.cast(), val.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,35 +63,21 @@ pub(super) fn define_int(names: &Names, name: &witx::Id, i: &witx::IntDatatype)
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
||||||
fn size() -> u32 {
|
fn guest_size() -> u32 {
|
||||||
::std::mem::size_of::<#repr>() as u32
|
#repr::guest_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align() -> u32 {
|
fn guest_align() -> usize {
|
||||||
::std::mem::align_of::<#repr>() as u32
|
#repr::guest_align()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> String {
|
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
||||||
stringify!(#ident).to_owned()
|
Ok(#ident(#repr::read(&location.cast())?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
fn write(location: &wiggle_runtime::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle_runtime::GuestError> {
|
||||||
use ::std::convert::TryFrom;
|
#repr::write(&location.cast(), val.0)
|
||||||
let raw: #repr = unsafe { (location.as_raw() as *const #repr).read() };
|
|
||||||
let _ = #ident::try_from(raw)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<#ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
|
||||||
Ok(*location.as_ref()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#ident>) {
|
|
||||||
let val: #repr = #repr::from(*self);
|
|
||||||
unsafe { (location.as_raw() as *mut #repr).write(val) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub fn define_datatype(names: &Names, namedtype: &witx::NamedType) -> TokenStrea
|
|||||||
witx::Type::Pointer(p) => define_witx_pointer(
|
witx::Type::Pointer(p) => define_witx_pointer(
|
||||||
names,
|
names,
|
||||||
&namedtype.name,
|
&namedtype.name,
|
||||||
quote!(wiggle_runtime::GuestPtrMut),
|
quote!(wiggle_runtime::GuestPtr),
|
||||||
p,
|
p,
|
||||||
),
|
),
|
||||||
witx::Type::ConstPointer(p) => {
|
witx::Type::ConstPointer(p) => {
|
||||||
@@ -71,7 +71,7 @@ fn define_witx_pointer(
|
|||||||
fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream {
|
fn define_witx_array(names: &Names, name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream {
|
||||||
let ident = names.type_(name);
|
let ident = names.type_(name);
|
||||||
let pointee_type = names.type_ref(arr_raw, quote!('a));
|
let pointee_type = names.type_ref(arr_raw, quote!('a));
|
||||||
quote!(pub type #ident<'a> = wiggle_runtime::GuestArray<'a, #pointee_type>;)
|
quote!(pub type #ident<'a> = wiggle_runtime::GuestPtr<'a, [#pointee_type]>;)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {
|
fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream {
|
||||||
|
|||||||
@@ -10,87 +10,9 @@ pub(super) fn define_struct(
|
|||||||
name: &witx::Id,
|
name: &witx::Id,
|
||||||
s: &witx::StructDatatype,
|
s: &witx::StructDatatype,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
if !s.needs_lifetime() {
|
|
||||||
define_copy_struct(names, name, s)
|
|
||||||
} else {
|
|
||||||
define_ptr_struct(names, name, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream {
|
|
||||||
let ident = names.type_(name);
|
let ident = names.type_(name);
|
||||||
let size = s.mem_size_align().size as u32;
|
let size = s.mem_size_align().size as u32;
|
||||||
let align = s.mem_size_align().align as u32;
|
let align = s.mem_size_align().align as usize;
|
||||||
|
|
||||||
let member_decls = s.members.iter().map(|m| {
|
|
||||||
let name = names.struct_member(&m.name);
|
|
||||||
let type_ = names.type_ref(&m.tref, anon_lifetime());
|
|
||||||
quote!(pub #name: #type_)
|
|
||||||
});
|
|
||||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
|
||||||
let type_ = names.type_ref(&ml.member.tref, anon_lifetime());
|
|
||||||
let offset = ml.offset as u32;
|
|
||||||
let fieldname = names.struct_member(&ml.member.name);
|
|
||||||
quote! {
|
|
||||||
#type_::validate(
|
|
||||||
&ptr.cast(#offset).map_err(|e|
|
|
||||||
wiggle_runtime::GuestError::InDataField{
|
|
||||||
typename: stringify!(#ident).to_owned(),
|
|
||||||
field: stringify!(#fieldname).to_owned(),
|
|
||||||
err: Box::new(e),
|
|
||||||
})?
|
|
||||||
).map_err(|e|
|
|
||||||
wiggle_runtime::GuestError::InDataField {
|
|
||||||
typename: stringify!(#ident).to_owned(),
|
|
||||||
field: stringify!(#fieldname).to_owned(),
|
|
||||||
err: Box::new(e),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub struct #ident {
|
|
||||||
#(#member_decls),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
|
||||||
fn size() -> u32 {
|
|
||||||
#size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align() -> u32 {
|
|
||||||
#align
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#ident>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
#(#member_valids)*
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<#ident, wiggle_runtime::GuestError> {
|
|
||||||
let r = location.as_ref()?;
|
|
||||||
Ok(*r)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
|
||||||
unsafe { (location.as_raw() as *mut #ident).write(*self) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestTypeTransparent<'a> for #ident {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -> TokenStream {
|
|
||||||
let ident = names.type_(name);
|
|
||||||
let size = s.mem_size_align().size as u32;
|
|
||||||
let align = s.mem_size_align().align as u32;
|
|
||||||
|
|
||||||
let member_names = s.members.iter().map(|m| names.struct_member(&m.name));
|
let member_names = s.members.iter().map(|m| names.struct_member(&m.name));
|
||||||
let member_decls = s.members.iter().map(|m| {
|
let member_decls = s.members.iter().map(|m| {
|
||||||
@@ -99,11 +21,7 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
|
|||||||
witx::TypeRef::Name(nt) => names.type_(&nt.name),
|
witx::TypeRef::Name(nt) => names.type_(&nt.name),
|
||||||
witx::TypeRef::Value(ty) => match &**ty {
|
witx::TypeRef::Value(ty) => match &**ty {
|
||||||
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
|
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
|
||||||
witx::Type::Pointer(pointee) => {
|
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
|
||||||
quote!(wiggle_runtime::GuestPtrMut<'a, #pointee_type>)
|
|
||||||
}
|
|
||||||
witx::Type::ConstPointer(pointee) => {
|
|
||||||
let pointee_type = names.type_ref(&pointee, quote!('a));
|
let pointee_type = names.type_ref(&pointee, quote!('a));
|
||||||
quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>)
|
quote!(wiggle_runtime::GuestPtr<'a, #pointee_type>)
|
||||||
}
|
}
|
||||||
@@ -112,68 +30,29 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
|
|||||||
};
|
};
|
||||||
quote!(pub #name: #type_)
|
quote!(pub #name: #type_)
|
||||||
});
|
});
|
||||||
let member_valids = s.member_layout().into_iter().map(|ml| {
|
|
||||||
let type_ = match &ml.member.tref {
|
|
||||||
witx::TypeRef::Name(nt) => names.type_(&nt.name),
|
|
||||||
witx::TypeRef::Value(ty) => match &**ty {
|
|
||||||
witx::Type::Builtin(builtin) => names.builtin_type(*builtin, quote!('a)),
|
|
||||||
witx::Type::Pointer(pointee) => {
|
|
||||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
|
||||||
quote!(wiggle_runtime::GuestPtrMut::<#pointee_type>)
|
|
||||||
}
|
|
||||||
witx::Type::ConstPointer(pointee) => {
|
|
||||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
|
||||||
quote!(wiggle_runtime::GuestPtr::<#pointee_type>)
|
|
||||||
}
|
|
||||||
_ => unimplemented!("other anonymous struct members"),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let offset = ml.offset as u32;
|
|
||||||
let fieldname = names.struct_member(&ml.member.name);
|
|
||||||
quote! {
|
|
||||||
#type_::validate(
|
|
||||||
&ptr.cast(#offset).map_err(|e|
|
|
||||||
wiggle_runtime::GuestError::InDataField{
|
|
||||||
typename: stringify!(#ident).to_owned(),
|
|
||||||
field: stringify!(#fieldname).to_owned(),
|
|
||||||
err: Box::new(e),
|
|
||||||
})?
|
|
||||||
).map_err(|e|
|
|
||||||
wiggle_runtime::GuestError::InDataField {
|
|
||||||
typename: stringify!(#ident).to_owned(),
|
|
||||||
field: stringify!(#fieldname).to_owned(),
|
|
||||||
err: Box::new(e),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let member_reads = s.member_layout().into_iter().map(|ml| {
|
let member_reads = s.member_layout().into_iter().map(|ml| {
|
||||||
let name = names.struct_member(&ml.member.name);
|
let name = names.struct_member(&ml.member.name);
|
||||||
let offset = ml.offset as u32;
|
let offset = ml.offset as u32;
|
||||||
|
let location = quote!(location.cast::<u8>().add(#offset)?.cast());
|
||||||
match &ml.member.tref {
|
match &ml.member.tref {
|
||||||
witx::TypeRef::Name(nt) => {
|
witx::TypeRef::Name(nt) => {
|
||||||
let type_ = names.type_(&nt.name);
|
let type_ = names.type_(&nt.name);
|
||||||
quote! {
|
quote! {
|
||||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
let #name = <#type_ as wiggle_runtime::GuestType>::read(&#location)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
witx::TypeRef::Value(ty) => match &**ty {
|
witx::TypeRef::Value(ty) => match &**ty {
|
||||||
witx::Type::Builtin(builtin) => {
|
witx::Type::Builtin(builtin) => {
|
||||||
let type_ = names.builtin_type(*builtin, anon_lifetime());
|
let type_ = names.builtin_type(*builtin, anon_lifetime());
|
||||||
quote! {
|
quote! {
|
||||||
let #name = <#type_ as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
let #name = <#type_ as wiggle_runtime::GuestType>::read(&#location)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
witx::Type::Pointer(pointee) => {
|
witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => {
|
||||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
||||||
quote! {
|
quote! {
|
||||||
let #name = <wiggle_runtime::GuestPtrMut::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
let #name = <wiggle_runtime::GuestPtr::<#pointee_type> as wiggle_runtime::GuestType>::read(&#location)?;
|
||||||
}
|
|
||||||
}
|
|
||||||
witx::Type::ConstPointer(pointee) => {
|
|
||||||
let pointee_type = names.type_ref(&pointee, anon_lifetime());
|
|
||||||
quote! {
|
|
||||||
let #name = <wiggle_runtime::GuestPtr::<#pointee_type> as wiggle_runtime::GuestType>::read(&location.cast(#offset)?)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!("other anonymous struct members"),
|
_ => unimplemented!("other anonymous struct members"),
|
||||||
@@ -185,41 +64,42 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) -
|
|||||||
let name = names.struct_member(&ml.member.name);
|
let name = names.struct_member(&ml.member.name);
|
||||||
let offset = ml.offset as u32;
|
let offset = ml.offset as u32;
|
||||||
quote! {
|
quote! {
|
||||||
wiggle_runtime::GuestType::write(&self.#name, &location.cast(#offset).expect("cast to inner member"));
|
wiggle_runtime::GuestType::write(
|
||||||
|
&location.cast::<u8>().add(#offset)?.cast(),
|
||||||
|
val.#name,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (struct_lifetime, extra_derive) = if s.needs_lifetime() {
|
||||||
|
(quote!(<'a>), quote!())
|
||||||
|
} else {
|
||||||
|
(quote!(), quote!(, Copy, PartialEq))
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug #extra_derive)]
|
||||||
pub struct #ident<'a> {
|
pub struct #ident #struct_lifetime {
|
||||||
#(#member_decls),*
|
#(#member_decls),*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident<'a> {
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident #struct_lifetime {
|
||||||
fn size() -> u32 {
|
fn guest_size() -> u32 {
|
||||||
#size
|
#size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align() -> u32 {
|
fn guest_align() -> usize {
|
||||||
#align
|
#align
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name() -> String {
|
fn read(location: &wiggle_runtime::GuestPtr<'a, Self>) -> Result<Self, wiggle_runtime::GuestError> {
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
#(#member_valids)*
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident<'a>>) -> Result<#ident<'a>, wiggle_runtime::GuestError> {
|
|
||||||
#(#member_reads)*
|
#(#member_reads)*
|
||||||
Ok(#ident { #(#member_names),* })
|
Ok(#ident { #(#member_names),* })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, Self>) {
|
fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle_runtime::GuestError> {
|
||||||
#(#member_writes)*
|
#(#member_writes)*
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::lifetimes::{anon_lifetime, LifetimeExt};
|
use crate::lifetimes::LifetimeExt;
|
||||||
use crate::names::Names;
|
use crate::names::Names;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
@@ -8,7 +8,7 @@ use witx::Layout;
|
|||||||
pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream {
|
pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDatatype) -> TokenStream {
|
||||||
let ident = names.type_(name);
|
let ident = names.type_(name);
|
||||||
let size = u.mem_size_align().size as u32;
|
let size = u.mem_size_align().size as u32;
|
||||||
let align = u.mem_size_align().align as u32;
|
let align = u.mem_size_align().align as usize;
|
||||||
let ulayout = u.union_layout();
|
let ulayout = u.union_layout();
|
||||||
let contents_offset = ulayout.contents_offset as u32;
|
let contents_offset = ulayout.contents_offset as u32;
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty
|
|||||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||||
quote! {
|
quote! {
|
||||||
#tagname::#variantname => {
|
#tagname::#variantname => {
|
||||||
let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
|
||||||
let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr)?;
|
let variant_val = <#varianttype as wiggle_runtime::GuestType>::read(&variant_ptr.cast())?;
|
||||||
Ok(#ident::#variantname(variant_val))
|
Ok(#ident::#variantname(variant_val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,17 +45,15 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty
|
|||||||
let write_variant = u.variants.iter().map(|v| {
|
let write_variant = u.variants.iter().map(|v| {
|
||||||
let variantname = names.enum_variant(&v.name);
|
let variantname = names.enum_variant(&v.name);
|
||||||
let write_tag = quote! {
|
let write_tag = quote! {
|
||||||
let tag_ptr = location.cast::<#tagname>(0).expect("union tag ptr TODO error report");
|
location.cast().write(#tagname::#variantname)?;
|
||||||
let mut tag_ref = tag_ptr.as_ref_mut().expect("union tag ref TODO error report");
|
|
||||||
*tag_ref = #tagname::#variantname;
|
|
||||||
};
|
};
|
||||||
if let Some(tref) = &v.tref {
|
if let Some(tref) = &v.tref {
|
||||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
let varianttype = names.type_ref(tref, lifetime.clone());
|
||||||
quote! {
|
quote! {
|
||||||
#ident::#variantname(contents) => {
|
#ident::#variantname(contents) => {
|
||||||
#write_tag
|
#write_tag
|
||||||
let variant_ptr = location.cast::<#varianttype>(#contents_offset).expect("union variant ptr validated");
|
let variant_ptr = location.cast::<u8>().add(#contents_offset)?;
|
||||||
<#varianttype as wiggle_runtime::GuestType>::write(&contents, &variant_ptr);
|
<#varianttype as wiggle_runtime::GuestType>::write(&variant_ptr.cast(), contents)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -66,134 +64,46 @@ pub(super) fn define_union(names: &Names, name: &witx::Id, u: &witx::UnionDataty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let validate = union_validate(names, ident.clone(), u, &ulayout);
|
|
||||||
|
|
||||||
if !u.needs_lifetime() {
|
let (enum_lifetime, extra_derive) = if u.needs_lifetime() {
|
||||||
// Type does not have a lifetime parameter:
|
(quote!(<'a>), quote!())
|
||||||
quote! {
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum #ident {
|
|
||||||
#(#variants),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> wiggle_runtime::GuestType<'a> for #ident {
|
|
||||||
fn size() -> u32 {
|
|
||||||
#size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align() -> u32 {
|
|
||||||
#align
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(ptr: &wiggle_runtime::GuestPtr<'a, #ident>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
#validate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<'a, #ident>)
|
|
||||||
-> Result<Self, wiggle_runtime::GuestError> {
|
|
||||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
|
||||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
|
||||||
match tag {
|
|
||||||
#(#read_variant)*
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<'a, #ident>) {
|
|
||||||
match self {
|
|
||||||
#(#write_variant)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
(quote!(), quote!(, Copy, PartialEq))
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum #ident<#lifetime> {
|
|
||||||
#(#variants),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<#lifetime> wiggle_runtime::GuestType<#lifetime> for #ident<#lifetime> {
|
|
||||||
fn size() -> u32 {
|
|
||||||
#size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align() -> u32 {
|
|
||||||
#align
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
stringify!(#ident).to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(ptr: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>) -> Result<(), wiggle_runtime::GuestError> {
|
|
||||||
#validate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(location: &wiggle_runtime::GuestPtr<#lifetime, #ident<#lifetime>>)
|
|
||||||
-> Result<Self, wiggle_runtime::GuestError> {
|
|
||||||
<#ident as wiggle_runtime::GuestType>::validate(location)?;
|
|
||||||
let tag = *location.cast::<#tagname>(0).expect("validated tag ptr").as_ref().expect("validated tag ref");
|
|
||||||
match tag {
|
|
||||||
#(#read_variant)*
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &wiggle_runtime::GuestPtrMut<#lifetime, #ident<#lifetime>>) {
|
|
||||||
match self {
|
|
||||||
#(#write_variant)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union_validate(
|
|
||||||
names: &Names,
|
|
||||||
typename: TokenStream,
|
|
||||||
u: &witx::UnionDatatype,
|
|
||||||
ulayout: &witx::UnionLayout,
|
|
||||||
) -> TokenStream {
|
|
||||||
let tagname = names.type_(&u.tag.name);
|
|
||||||
let contents_offset = ulayout.contents_offset as u32;
|
|
||||||
|
|
||||||
let with_err = |f: &str| -> TokenStream {
|
|
||||||
quote!(|e| wiggle_runtime::GuestError::InDataField {
|
|
||||||
typename: stringify!(#typename).to_owned(),
|
|
||||||
field: #f.to_owned(),
|
|
||||||
err: Box::new(e),
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tag_err = with_err("<tag>");
|
|
||||||
let variant_validation = u.variants.iter().map(|v| {
|
|
||||||
let err = with_err(v.name.as_str());
|
|
||||||
let variantname = names.enum_variant(&v.name);
|
|
||||||
if let Some(tref) = &v.tref {
|
|
||||||
let lifetime = anon_lifetime();
|
|
||||||
let varianttype = names.type_ref(tref, lifetime.clone());
|
|
||||||
quote! {
|
|
||||||
#tagname::#variantname => {
|
|
||||||
let variant_ptr = ptr.cast::<#varianttype>(#contents_offset).map_err(#err)?;
|
|
||||||
<#varianttype as wiggle_runtime::GuestType>::validate(&variant_ptr).map_err(#err)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! { #tagname::#variantname => {} }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
let tag = *ptr.cast::<#tagname>(0).map_err(#tag_err)?.as_ref().map_err(#tag_err)?;
|
#[derive(Clone, Debug #extra_derive)]
|
||||||
match tag {
|
pub enum #ident #enum_lifetime {
|
||||||
#(#variant_validation)*
|
#(#variants),*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> wiggle_runtime::GuestType<'a> for #ident #enum_lifetime {
|
||||||
|
fn guest_size() -> u32 {
|
||||||
|
#size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guest_align() -> usize {
|
||||||
|
#align
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(location: &wiggle_runtime::GuestPtr<'a, Self>)
|
||||||
|
-> Result<Self, wiggle_runtime::GuestError>
|
||||||
|
{
|
||||||
|
let tag = location.cast().read()?;
|
||||||
|
match tag {
|
||||||
|
#(#read_variant)*
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(location: &wiggle_runtime::GuestPtr<'_, Self>, val: Self)
|
||||||
|
-> Result<(), wiggle_runtime::GuestError>
|
||||||
|
{
|
||||||
|
match val {
|
||||||
|
#(#write_variant)*
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::region::Region;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct BorrowHandle(usize);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GuestBorrows {
|
|
||||||
immutable: HashMap<BorrowHandle, Region>,
|
|
||||||
mutable: HashMap<BorrowHandle, Region>,
|
|
||||||
next_handle: BorrowHandle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GuestBorrows {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
GuestBorrows {
|
|
||||||
immutable: HashMap::new(),
|
|
||||||
mutable: HashMap::new(),
|
|
||||||
next_handle: BorrowHandle(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_borrowed_immut(&self, r: Region) -> bool {
|
|
||||||
!self.immutable.values().all(|b| !b.overlaps(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_borrowed_mut(&self, r: Region) -> bool {
|
|
||||||
!self.mutable.values().all(|b| !b.overlaps(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_handle(&mut self) -> BorrowHandle {
|
|
||||||
let h = self.next_handle;
|
|
||||||
self.next_handle = BorrowHandle(h.0 + 1);
|
|
||||||
h
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow_immut(&mut self, r: Region) -> Option<BorrowHandle> {
|
|
||||||
if self.is_borrowed_mut(r) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let h = self.new_handle();
|
|
||||||
self.immutable.insert(h, r);
|
|
||||||
Some(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unborrow_immut(&mut self, h: BorrowHandle) {
|
|
||||||
self.immutable
|
|
||||||
.remove(&h)
|
|
||||||
.expect("handle exists in immutable borrows");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow_mut(&mut self, r: Region) -> Option<BorrowHandle> {
|
|
||||||
if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let h = self.new_handle();
|
|
||||||
self.mutable.insert(h, r);
|
|
||||||
Some(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unborrow_mut(&mut self, h: BorrowHandle) {
|
|
||||||
self.mutable
|
|
||||||
.remove(&h)
|
|
||||||
.expect("handle exists in mutable borrows");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +1,80 @@
|
|||||||
use crate::{GuestError, GuestPtr, GuestPtrMut};
|
use crate::{GuestError, GuestPtr};
|
||||||
|
use std::mem;
|
||||||
pub trait GuestType<'a>: Sized + Clone {
|
|
||||||
// These are morally the same as Rust ::std::mem::size_of / align_of, but they return
|
|
||||||
// a u32 because the wasm memory space is 32 bits. They have a different names so they
|
|
||||||
// don't collide with the std::mem methods.
|
|
||||||
fn size() -> u32;
|
|
||||||
fn align() -> u32;
|
|
||||||
fn name() -> String;
|
|
||||||
fn validate(location: &GuestPtr<'a, Self>) -> Result<(), GuestError>;
|
|
||||||
fn read(location: &GuestPtr<'a, Self>) -> Result<Self, GuestError>;
|
|
||||||
fn write(&self, location: &GuestPtrMut<'a, Self>);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents any guest type which can transparently be represented
|
|
||||||
/// as a host type.
|
|
||||||
pub trait GuestTypeTransparent<'a>: GuestType<'a> + Copy {}
|
|
||||||
|
|
||||||
macro_rules! builtin_type {
|
|
||||||
( $( $t:ident ), * ) => {
|
|
||||||
$(
|
|
||||||
impl<'a> GuestType<'a> for $t {
|
|
||||||
fn size() -> u32 {
|
|
||||||
::std::mem::size_of::<$t>() as u32
|
|
||||||
}
|
|
||||||
fn align() -> u32 {
|
|
||||||
::std::mem::align_of::<$t>() as u32
|
|
||||||
}
|
|
||||||
fn name() -> String {
|
|
||||||
::std::stringify!($t).to_owned()
|
|
||||||
}
|
|
||||||
fn validate(_ptr: &GuestPtr<$t>) -> Result<(), GuestError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn read(location: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
|
|
||||||
Ok(*location.as_ref()?)
|
|
||||||
}
|
|
||||||
fn write(&self, location: &GuestPtrMut<'a, Self>) {
|
|
||||||
unsafe { (location.as_raw() as *mut $t).write(*self) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> GuestTypeTransparent<'a> for $t {}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// These definitions correspond to all the witx BuiltinType variants that are Copy:
|
|
||||||
builtin_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize);
|
|
||||||
|
|
||||||
pub trait GuestErrorType {
|
pub trait GuestErrorType {
|
||||||
type Context;
|
type Context;
|
||||||
fn success() -> Self;
|
fn success() -> Self;
|
||||||
fn from_error(e: GuestError, ctx: &Self::Context) -> Self;
|
fn from_error(e: GuestError, ctx: &Self::Context) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait GuestType<'a>: Sized {
|
||||||
|
fn guest_size() -> u32;
|
||||||
|
fn guest_align() -> usize;
|
||||||
|
fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError>;
|
||||||
|
fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! primitives {
|
||||||
|
($($i:ident)*) => ($(
|
||||||
|
impl<'a> GuestType<'a> for $i {
|
||||||
|
fn guest_size() -> u32 { mem::size_of::<Self>() as u32 }
|
||||||
|
fn guest_align() -> usize { mem::align_of::<Self>() }
|
||||||
|
|
||||||
|
fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
|
||||||
|
|
||||||
|
// Any bit pattern for any primitive implemented with this
|
||||||
|
// macro is safe, so our `as_raw` method will guarantee that if
|
||||||
|
// we are given a pointer it's valid for the size of our type
|
||||||
|
// as well as properly aligned. Consequently we should be able
|
||||||
|
// to safely ready the pointer just after we validated it,
|
||||||
|
// returning it along here.
|
||||||
|
let host_ptr = ptr.mem().validate_size_align(
|
||||||
|
ptr.offset(),
|
||||||
|
Self::guest_align(),
|
||||||
|
Self::guest_size(),
|
||||||
|
)?;
|
||||||
|
Ok(unsafe { *host_ptr.cast::<Self>() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> {
|
||||||
|
let host_ptr = ptr.mem().validate_size_align(
|
||||||
|
ptr.offset(),
|
||||||
|
Self::guest_align(),
|
||||||
|
Self::guest_size(),
|
||||||
|
)?;
|
||||||
|
// Similar to above `as_raw` will do a lot of validation, and
|
||||||
|
// then afterwards we can safely write our value into the
|
||||||
|
// memory location.
|
||||||
|
unsafe {
|
||||||
|
*host_ptr.cast::<Self>() = val;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
primitives! {
|
||||||
|
i8 i16 i32 i64 i128 isize
|
||||||
|
u8 u16 u32 u64 u128 usize
|
||||||
|
f32 f64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support pointers-to-pointers where pointers are always 32-bits in wasm land
|
||||||
|
impl<'a, T> GuestType<'a> for GuestPtr<'a, T> {
|
||||||
|
fn guest_size() -> u32 {
|
||||||
|
u32::guest_size()
|
||||||
|
}
|
||||||
|
fn guest_align() -> usize {
|
||||||
|
u32::guest_align()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(ptr: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
|
||||||
|
let offset = ptr.cast::<u32>().read()?;
|
||||||
|
Ok(GuestPtr::new(ptr.mem(), offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> {
|
||||||
|
ptr.cast::<u32>().write(val.offset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,217 @@
|
|||||||
mod borrow;
|
use std::cell::Cell;
|
||||||
|
use std::slice;
|
||||||
|
use std::str;
|
||||||
|
use std::marker;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod guest_type;
|
mod guest_type;
|
||||||
mod memory;
|
|
||||||
mod region;
|
mod region;
|
||||||
|
|
||||||
pub use error::GuestError;
|
pub use error::GuestError;
|
||||||
pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};
|
pub use guest_type::{GuestErrorType, GuestType};
|
||||||
pub use memory::{
|
|
||||||
GuestArray, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, GuestString,
|
|
||||||
GuestStringRef,
|
|
||||||
};
|
|
||||||
pub use region::Region;
|
pub use region::Region;
|
||||||
|
|
||||||
|
pub unsafe trait GuestMemory {
|
||||||
|
fn base(&self) -> (*mut u8, u32);
|
||||||
|
|
||||||
|
fn validate_size_align(
|
||||||
|
&self,
|
||||||
|
offset: u32,
|
||||||
|
align: usize,
|
||||||
|
len: u32,
|
||||||
|
) -> Result<*mut u8, GuestError> {
|
||||||
|
let (base_ptr, base_len) = self.base();
|
||||||
|
let region = Region { start: offset, len };
|
||||||
|
|
||||||
|
// Figure out our pointer to the start of memory
|
||||||
|
let start = match (base_ptr as usize).checked_add(offset as usize) {
|
||||||
|
Some(ptr) => ptr,
|
||||||
|
None => return Err(GuestError::PtrOutOfBounds(region)),
|
||||||
|
};
|
||||||
|
// and use that to figure out the end pointer
|
||||||
|
let end = match start.checked_add(len as usize) {
|
||||||
|
Some(ptr) => ptr,
|
||||||
|
None => return Err(GuestError::PtrOutOfBounds(region)),
|
||||||
|
};
|
||||||
|
// and then verify that our end doesn't reach past the end of our memory
|
||||||
|
if end > (base_ptr as usize) + (base_len as usize) {
|
||||||
|
return Err(GuestError::PtrOutOfBounds(region));
|
||||||
|
}
|
||||||
|
// and finally verify that the alignment is correct
|
||||||
|
if start % align != 0 {
|
||||||
|
return Err(GuestError::PtrNotAligned(region, align as u32));
|
||||||
|
}
|
||||||
|
Ok(start as *mut u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: ?Sized + Pointee,
|
||||||
|
{
|
||||||
|
GuestPtr::new(self, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T {
|
||||||
|
fn base(&self) -> (*mut u8, u32) {
|
||||||
|
T::base(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T {
|
||||||
|
fn base(&self) -> (*mut u8, u32) {
|
||||||
|
T::base(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GuestPtr<'a, T: ?Sized + Pointee> {
|
||||||
|
mem: &'a (dyn GuestMemory + 'a),
|
||||||
|
pointer: T::Pointer,
|
||||||
|
_marker: marker::PhantomData<&'a Cell<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> {
|
||||||
|
pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'_, T> {
|
||||||
|
GuestPtr {
|
||||||
|
mem,
|
||||||
|
pointer,
|
||||||
|
_marker: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> T::Pointer {
|
||||||
|
self.pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mem(&self) -> &'a (dyn GuestMemory + 'a) {
|
||||||
|
self.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast<U>(&self) -> GuestPtr<'a, U>
|
||||||
|
where
|
||||||
|
T: Pointee<Pointer = u32>,
|
||||||
|
{
|
||||||
|
GuestPtr::new(self.mem, self.pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self) -> Result<T, GuestError>
|
||||||
|
where
|
||||||
|
T: GuestType<'a>,
|
||||||
|
{
|
||||||
|
T::read(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, val: T) -> Result<(), GuestError>
|
||||||
|
where
|
||||||
|
T: GuestType<'a>,
|
||||||
|
{
|
||||||
|
T::write(self, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, amt: u32) -> Result<GuestPtr<'a, T>, GuestError>
|
||||||
|
where T: GuestType<'a> + Pointee<Pointer = u32>,
|
||||||
|
{
|
||||||
|
let offset = amt.checked_mul(T::guest_size())
|
||||||
|
.and_then(|o| self.pointer.checked_add(o));
|
||||||
|
let offset = match offset {
|
||||||
|
Some(o) => o,
|
||||||
|
None => return Err(GuestError::InvalidFlagValue("")),
|
||||||
|
};
|
||||||
|
Ok(GuestPtr::new(self.mem, offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> GuestPtr<'a, [T]> {
|
||||||
|
pub fn offset_base(&self) -> u32 {
|
||||||
|
self.pointer.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> u32 {
|
||||||
|
self.pointer.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'b>(&'b self) -> impl ExactSizeIterator<Item = Result<GuestPtr<'a, T>, GuestError>> + 'b
|
||||||
|
where
|
||||||
|
T: GuestType<'a>,
|
||||||
|
{
|
||||||
|
let base = GuestPtr::new(self.mem, self.offset_base());
|
||||||
|
(0..self.len()).map(move |i| base.add(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GuestPtr<'a, str> {
|
||||||
|
pub fn offset_base(&self) -> u32 {
|
||||||
|
self.pointer.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> u32 {
|
||||||
|
self.pointer.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> GuestPtr<'a, [u8]> {
|
||||||
|
GuestPtr::new(self.mem, self.pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_raw(&self) -> Result<*mut str, GuestError> {
|
||||||
|
let ptr = self.mem.validate_size_align(self.pointer.0, 1, self.pointer.1)?;
|
||||||
|
|
||||||
|
// TODO: doc unsafety here
|
||||||
|
unsafe {
|
||||||
|
let s = slice::from_raw_parts_mut(ptr, self.pointer.1 as usize);
|
||||||
|
match str::from_utf8_mut(s) {
|
||||||
|
Ok(s) => Ok(s),
|
||||||
|
Err(e) => Err(GuestError::InvalidUtf8(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Pointee> Clone for GuestPtr<'_, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Pointee> Copy for GuestPtr<'_, T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
T::debug(self.pointer, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
pub trait Sealed {}
|
||||||
|
impl<T> Sealed for T {}
|
||||||
|
impl<T> Sealed for [T] {}
|
||||||
|
impl Sealed for str {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Pointee: private::Sealed {
|
||||||
|
#[doc(hidden)]
|
||||||
|
type Pointer: Copy;
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pointee for T {
|
||||||
|
type Pointer = u32;
|
||||||
|
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "*guest {:#x}", pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pointee for [T] {
|
||||||
|
type Pointer = (u32, u32);
|
||||||
|
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pointee for str {
|
||||||
|
type Pointer = (u32, u32);
|
||||||
|
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
<[u8]>::debug(pointer, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,260 +0,0 @@
|
|||||||
use super::ptr::{GuestPtr, GuestRef};
|
|
||||||
use crate::{GuestError, GuestType, GuestTypeTransparent};
|
|
||||||
use std::{fmt, ops::Deref};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GuestArray<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
pub(super) ptr: GuestPtr<'a, T>,
|
|
||||||
pub(super) num_elems: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestArray<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a> + fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestArray {{ ptr: {:?}, num_elems: {:?} }}",
|
|
||||||
self.ptr, self.num_elems
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestArray<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
pub fn iter(&self) -> GuestArrayIter<'a, T> {
|
|
||||||
let next = GuestPtr {
|
|
||||||
mem: self.ptr.mem,
|
|
||||||
region: self.ptr.region,
|
|
||||||
type_: self.ptr.type_,
|
|
||||||
};
|
|
||||||
GuestArrayIter {
|
|
||||||
next,
|
|
||||||
num_elems: self.num_elems,
|
|
||||||
count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuestArrayIter<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
next: GuestPtr<'a, T>,
|
|
||||||
num_elems: u32,
|
|
||||||
count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for GuestArrayIter<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
type Item = Result<GuestPtr<'a, T>, GuestError>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.count < self.num_elems {
|
|
||||||
// ok...
|
|
||||||
Some(T::validate(&self.next).and_then(|()| {
|
|
||||||
let curr = GuestPtr {
|
|
||||||
mem: self.next.mem,
|
|
||||||
region: self.next.region,
|
|
||||||
type_: self.next.type_,
|
|
||||||
};
|
|
||||||
self.next = self.next.elem(1)?;
|
|
||||||
self.count += 1;
|
|
||||||
Ok(curr)
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
// no more elements...
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestArray<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
pub fn as_ref(&self) -> Result<GuestArrayRef<'a, T>, GuestError> {
|
|
||||||
let mut next = self.ptr.elem(0)?;
|
|
||||||
for _ in 0..self.num_elems {
|
|
||||||
T::validate(&next)?;
|
|
||||||
next = next.elem(1)?;
|
|
||||||
}
|
|
||||||
let region = self.ptr.region.extend(self.num_elems);
|
|
||||||
let handle = {
|
|
||||||
let mut borrows = self.ptr.mem.borrows.borrow_mut();
|
|
||||||
borrows
|
|
||||||
.borrow_immut(region)
|
|
||||||
.ok_or_else(|| GuestError::PtrBorrowed(region))?
|
|
||||||
};
|
|
||||||
let ref_ = GuestRef {
|
|
||||||
mem: self.ptr.mem,
|
|
||||||
region,
|
|
||||||
handle,
|
|
||||||
type_: self.ptr.type_,
|
|
||||||
};
|
|
||||||
Ok(GuestArrayRef {
|
|
||||||
ref_,
|
|
||||||
num_elems: self.num_elems,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuestArrayRef<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
ref_: GuestRef<'a, T>,
|
|
||||||
num_elems: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestArrayRef<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a> + fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestArrayRef {{ ref_: {:?}, num_elems: {:?} }}",
|
|
||||||
self.ref_, self.num_elems
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for GuestArrayRef<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe {
|
|
||||||
std::slice::from_raw_parts(
|
|
||||||
self.ref_.as_ptr().as_raw() as *const _,
|
|
||||||
self.num_elems as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::{
|
|
||||||
memory::ptr::{GuestPtr, GuestPtrMut},
|
|
||||||
GuestError, GuestMemory, GuestType, Region,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(align(4096))]
|
|
||||||
struct HostMemory {
|
|
||||||
buffer: [u8; 4096],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HostMemory {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { buffer: [0; 4096] }
|
|
||||||
}
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
|
||||||
self.buffer.as_mut_ptr()
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn out_of_bounds() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// try extracting an array out of memory bounds
|
|
||||||
let ptr: GuestPtr<i32> = guest_memory.ptr(4092).expect("ptr to last i32 el");
|
|
||||||
let err = ptr.array(2).expect_err("out of bounds ptr error");
|
|
||||||
assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4092, 8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ptr_to_array() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// write a simple array into memory
|
|
||||||
{
|
|
||||||
let ptr: GuestPtrMut<i32> = guest_memory.ptr_mut(0).expect("ptr mut to first el");
|
|
||||||
let mut el = ptr.as_ref_mut().expect("ref mut to first el");
|
|
||||||
*el = 1;
|
|
||||||
let ptr: GuestPtrMut<i32> = guest_memory.ptr_mut(4).expect("ptr mut to second el");
|
|
||||||
let mut el = ptr.as_ref_mut().expect("ref mu to second el");
|
|
||||||
*el = 2;
|
|
||||||
let ptr: GuestPtrMut<i32> = guest_memory.ptr_mut(8).expect("ptr mut to third el");
|
|
||||||
let mut el = ptr.as_ref_mut().expect("ref mut to third el");
|
|
||||||
*el = 3;
|
|
||||||
}
|
|
||||||
// extract as array
|
|
||||||
let ptr: GuestPtr<i32> = guest_memory.ptr(0).expect("ptr to first el");
|
|
||||||
let arr = ptr.array(3).expect("convert ptr to array");
|
|
||||||
let as_ref = &*arr.as_ref().expect("array borrowed immutably");
|
|
||||||
assert_eq!(as_ref, &[1, 2, 3]);
|
|
||||||
// borrowing again should be fine
|
|
||||||
let as_ref2 = &*arr.as_ref().expect("array borrowed immutably again");
|
|
||||||
assert_eq!(as_ref2, as_ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ptr_to_ptr_array() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
{
|
|
||||||
let val_ptr: GuestPtrMut<u8> =
|
|
||||||
guest_memory.ptr_mut(0).expect("ptr mut to the first value");
|
|
||||||
let mut val = val_ptr.as_ref_mut().expect("ref mut to the first value");
|
|
||||||
*val = 255;
|
|
||||||
let val_ptr: GuestPtrMut<u8> = guest_memory
|
|
||||||
.ptr_mut(4)
|
|
||||||
.expect("ptr mut to the second value");
|
|
||||||
let mut val = val_ptr.as_ref_mut().expect("ref mut to the second value");
|
|
||||||
*val = 254;
|
|
||||||
let val_ptr: GuestPtrMut<u8> =
|
|
||||||
guest_memory.ptr_mut(8).expect("ptr mut to the third value");
|
|
||||||
let mut val = val_ptr.as_ref_mut().expect("ref mut to the third value");
|
|
||||||
*val = 253;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let ptr = guest_memory.ptr_mut(12).expect("ptr mut to first el");
|
|
||||||
ptr.write(
|
|
||||||
&guest_memory
|
|
||||||
.ptr::<GuestPtr<u8>>(0)
|
|
||||||
.expect("ptr to the first value"),
|
|
||||||
);
|
|
||||||
let ptr = guest_memory.ptr_mut(16).expect("ptr mut to first el");
|
|
||||||
ptr.write(
|
|
||||||
&guest_memory
|
|
||||||
.ptr::<GuestPtr<u8>>(4)
|
|
||||||
.expect("ptr to the second value"),
|
|
||||||
);
|
|
||||||
let ptr = guest_memory.ptr_mut(20).expect("ptr mut to first el");
|
|
||||||
ptr.write(
|
|
||||||
&guest_memory
|
|
||||||
.ptr::<GuestPtr<u8>>(8)
|
|
||||||
.expect("ptr to the third value"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// extract as array
|
|
||||||
let ptr: GuestPtr<GuestPtr<u8>> = guest_memory.ptr(12).expect("ptr to first el");
|
|
||||||
let arr = ptr.array(3).expect("convert ptr to array");
|
|
||||||
let contents = arr
|
|
||||||
.iter()
|
|
||||||
.map(|ptr_ptr| {
|
|
||||||
*GuestType::read(&ptr_ptr.expect("valid ptr to ptr"))
|
|
||||||
.expect("valid ptr to some value")
|
|
||||||
.as_ref()
|
|
||||||
.expect("deref ptr to some value")
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
assert_eq!(&contents, &[255, 254, 253]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
mod array;
|
|
||||||
mod ptr;
|
|
||||||
mod string;
|
|
||||||
|
|
||||||
pub use array::*;
|
|
||||||
pub use ptr::*;
|
|
||||||
pub use string::*;
|
|
||||||
|
|
||||||
use crate::{borrow::GuestBorrows, GuestError, GuestType, Region};
|
|
||||||
use std::{cell::RefCell, fmt, marker::PhantomData, rc::Rc};
|
|
||||||
|
|
||||||
pub struct GuestMemory<'a> {
|
|
||||||
ptr: *mut u8,
|
|
||||||
len: u32,
|
|
||||||
lifetime: PhantomData<&'a ()>,
|
|
||||||
borrows: Rc<RefCell<GuestBorrows>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for GuestMemory<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestMemory {{ ptr: {:?}, len: {:?}, borrows: {:?} }}",
|
|
||||||
self.ptr, self.len, self.borrows
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> GuestMemory<'a> {
|
|
||||||
pub fn new(ptr: *mut u8, len: u32) -> Self {
|
|
||||||
assert_eq!(ptr as usize % 4096, 0, "GuestMemory must be page-aligned");
|
|
||||||
Self {
|
|
||||||
ptr,
|
|
||||||
len,
|
|
||||||
lifetime: PhantomData,
|
|
||||||
borrows: Rc::new(RefCell::new(GuestBorrows::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, r: Region) -> bool {
|
|
||||||
r.start < self.len
|
|
||||||
&& r.len < self.len // make sure next clause doesnt underflow
|
|
||||||
&& r.start <= (self.len - r.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr<T: GuestType<'a>>(&'a self, at: u32) -> Result<GuestPtr<'a, T>, GuestError> {
|
|
||||||
let region = Region {
|
|
||||||
start: at,
|
|
||||||
len: T::size(),
|
|
||||||
};
|
|
||||||
if !self.contains(region) {
|
|
||||||
Err(GuestError::PtrOutOfBounds(region))?;
|
|
||||||
}
|
|
||||||
if at % T::align() != 0 {
|
|
||||||
Err(GuestError::PtrNotAligned(region, T::align()))?;
|
|
||||||
}
|
|
||||||
Ok(GuestPtr {
|
|
||||||
mem: &self,
|
|
||||||
region,
|
|
||||||
type_: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr_mut<T: GuestType<'a>>(&'a self, at: u32) -> Result<GuestPtrMut<'a, T>, GuestError> {
|
|
||||||
let ptr = self.ptr(at)?;
|
|
||||||
Ok(GuestPtrMut {
|
|
||||||
mem: ptr.mem,
|
|
||||||
region: ptr.region,
|
|
||||||
type_: ptr.type_,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,515 +0,0 @@
|
|||||||
use super::{array::GuestArray, GuestMemory};
|
|
||||||
use crate::{borrow::BorrowHandle, GuestError, GuestType, GuestTypeTransparent, Region};
|
|
||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
marker::PhantomData,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GuestPtr<'a, T> {
|
|
||||||
pub(super) mem: &'a GuestMemory<'a>,
|
|
||||||
pub(super) region: Region,
|
|
||||||
pub(super) type_: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestPtr<'a, T>
|
|
||||||
where
|
|
||||||
T: fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestPtr {{ mem: {:?}, region: {:?} }}",
|
|
||||||
self.mem, self.region
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: GuestType<'a>> GuestPtr<'a, T> {
|
|
||||||
pub fn as_raw(&self) -> *const u8 {
|
|
||||||
(self.mem.ptr as usize + self.region.start as usize) as *const u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn elem(&self, elements: i32) -> Result<Self, GuestError> {
|
|
||||||
self.mem
|
|
||||||
.ptr(self.region.start + (elements * self.region.len as i32) as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cast<CastTo: GuestType<'a>>(
|
|
||||||
&self,
|
|
||||||
offset: u32,
|
|
||||||
) -> Result<GuestPtr<'a, CastTo>, GuestError> {
|
|
||||||
self.mem.ptr(self.region.start + offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn array(&self, num_elems: u32) -> Result<GuestArray<'a, T>, GuestError> {
|
|
||||||
let region = self.region.extend(num_elems);
|
|
||||||
if self.mem.contains(region) {
|
|
||||||
let ptr = GuestPtr {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
type_: self.type_,
|
|
||||||
};
|
|
||||||
Ok(GuestArray { ptr, num_elems })
|
|
||||||
} else {
|
|
||||||
Err(GuestError::PtrOutOfBounds(region))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestPtr<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
pub fn as_ref(&self) -> Result<GuestRef<'a, T>, GuestError> {
|
|
||||||
T::validate(&self)?;
|
|
||||||
let handle = {
|
|
||||||
let mut borrows = self.mem.borrows.borrow_mut();
|
|
||||||
borrows
|
|
||||||
.borrow_immut(self.region)
|
|
||||||
.ok_or_else(|| GuestError::PtrBorrowed(self.region))?
|
|
||||||
};
|
|
||||||
Ok(GuestRef {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
handle,
|
|
||||||
type_: self.type_,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestPtr<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
pub fn read(&self) -> Result<T, GuestError> {
|
|
||||||
T::read(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestType<'a> for GuestPtr<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
fn size() -> u32 {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align() -> u32 {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
format!("GuestPtr<{}>", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(location: &GuestPtr<'a, GuestPtr<'a, T>>) -> Result<(), GuestError> {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) };
|
|
||||||
// GuestMemory can validate that the raw pointer contents are legal for T:
|
|
||||||
let _guest_ptr: GuestPtr<T> = location.mem.ptr(raw_ptr)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operations for reading and writing Ptrs to memory:
|
|
||||||
fn read(location: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) };
|
|
||||||
// GuestMemory can validate that the raw pointer contents are legal for T:
|
|
||||||
let guest_ptr: GuestPtr<'a, T> = location.mem.ptr(raw_ptr)?;
|
|
||||||
Ok(guest_ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &GuestPtrMut<'a, Self>) {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
unsafe {
|
|
||||||
let raw_ptr: *mut u32 = location.as_raw() as *mut u32;
|
|
||||||
raw_ptr.write(self.region.start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GuestPtrMut<'a, T> {
|
|
||||||
pub(super) mem: &'a GuestMemory<'a>,
|
|
||||||
pub(super) region: Region,
|
|
||||||
pub(super) type_: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestPtrMut<'a, T>
|
|
||||||
where
|
|
||||||
T: fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestPtrMut {{ mem: {:?}, region: {:?} }}",
|
|
||||||
self.mem, self.region
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestPtrMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
pub fn as_immut(&self) -> GuestPtr<'a, T> {
|
|
||||||
GuestPtr {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
type_: self.type_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_raw(&self) -> *mut u8 {
|
|
||||||
(self.mem.ptr as usize + self.region.start as usize) as *mut u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn elem(&self, elements: i32) -> Result<Self, GuestError> {
|
|
||||||
self.mem
|
|
||||||
.ptr_mut(self.region.start + (elements * self.region.len as i32) as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cast<CastTo: GuestType<'a>>(
|
|
||||||
&self,
|
|
||||||
offset: u32,
|
|
||||||
) -> Result<GuestPtrMut<'a, CastTo>, GuestError> {
|
|
||||||
self.mem.ptr_mut(self.region.start + offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestPtrMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
pub fn as_ref(&self) -> Result<GuestRef<'a, T>, GuestError> {
|
|
||||||
self.as_immut().as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_ref_mut(&self) -> Result<GuestRefMut<'a, T>, GuestError> {
|
|
||||||
T::validate(&self.as_immut())?;
|
|
||||||
let handle = {
|
|
||||||
let mut borrows = self.mem.borrows.borrow_mut();
|
|
||||||
borrows
|
|
||||||
.borrow_mut(self.region)
|
|
||||||
.ok_or_else(|| GuestError::PtrBorrowed(self.region))?
|
|
||||||
};
|
|
||||||
Ok(GuestRefMut {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
handle,
|
|
||||||
type_: self.type_,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestPtrMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
pub fn read(&self) -> Result<T, GuestError> {
|
|
||||||
T::read(&self.as_immut())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&self, ptr: &T) {
|
|
||||||
T::write(ptr, &self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestType<'a> for GuestPtrMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestType<'a>,
|
|
||||||
{
|
|
||||||
fn size() -> u32 {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align() -> u32 {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
format!("GuestPtrMut<{}>", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(location: &GuestPtr<'a, GuestPtrMut<'a, T>>) -> Result<(), GuestError> {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) };
|
|
||||||
// GuestMemory can validate that the raw pointer contents are legal for T:
|
|
||||||
let _guest_ptr: GuestPtr<T> = location.mem.ptr(raw_ptr)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading and writing GuestPtrMuts to memory:
|
|
||||||
fn read(location: &GuestPtr<'a, Self>) -> Result<Self, GuestError> {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
let raw_ptr: u32 = unsafe { *(location.as_raw() as *const u32) };
|
|
||||||
// GuestMemory can validate that the raw pointer contents are legal for T:
|
|
||||||
let guest_ptr_mut: GuestPtrMut<'a, T> = location.mem.ptr_mut(raw_ptr)?;
|
|
||||||
Ok(guest_ptr_mut)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, location: &GuestPtrMut<'a, Self>) {
|
|
||||||
// location is guaranteed to be in GuestMemory and aligned to 4
|
|
||||||
unsafe {
|
|
||||||
let raw_ptr: *mut u32 = location.as_raw() as *mut u32;
|
|
||||||
raw_ptr.write(self.region.start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuestRef<'a, T> {
|
|
||||||
pub(super) mem: &'a GuestMemory<'a>,
|
|
||||||
pub(super) region: Region,
|
|
||||||
pub(super) handle: BorrowHandle,
|
|
||||||
pub(super) type_: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestRef<'a, T>
|
|
||||||
where
|
|
||||||
T: fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestRef {{ mem: {:?}, region: {:?}, handle: {:?} }}",
|
|
||||||
self.mem, self.region, self.handle
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestRef<'a, T> {
|
|
||||||
pub fn as_ptr(&self) -> GuestPtr<'a, T> {
|
|
||||||
GuestPtr {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
type_: self.type_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for GuestRef<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe {
|
|
||||||
((self.mem.ptr as usize + self.region.start as usize) as *const T)
|
|
||||||
.as_ref()
|
|
||||||
.expect("GuestRef implies non-null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Drop for GuestRef<'a, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut borrows = self.mem.borrows.borrow_mut();
|
|
||||||
borrows.unborrow_immut(self.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuestRefMut<'a, T> {
|
|
||||||
pub(super) mem: &'a GuestMemory<'a>,
|
|
||||||
pub(super) region: Region,
|
|
||||||
pub(super) handle: BorrowHandle,
|
|
||||||
pub(super) type_: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> fmt::Debug for GuestRefMut<'a, T>
|
|
||||||
where
|
|
||||||
T: fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"GuestRefMut {{ mem: {:?}, region: {:?}, handle: {:?} }}",
|
|
||||||
self.mem, self.region, self.handle
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> GuestRefMut<'a, T> {
|
|
||||||
pub fn as_ptr(&self) -> GuestPtr<'a, T> {
|
|
||||||
GuestPtr {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
type_: self.type_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_ptr_mut(&self) -> GuestPtrMut<'a, T> {
|
|
||||||
GuestPtrMut {
|
|
||||||
mem: self.mem,
|
|
||||||
region: self.region,
|
|
||||||
type_: self.type_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> ::std::ops::Deref for GuestRefMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe {
|
|
||||||
((self.mem.ptr as usize + self.region.start as usize) as *const T)
|
|
||||||
.as_ref()
|
|
||||||
.expect("GuestRef implies non-null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> DerefMut for GuestRefMut<'a, T>
|
|
||||||
where
|
|
||||||
T: GuestTypeTransparent<'a>,
|
|
||||||
{
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe {
|
|
||||||
((self.mem.ptr as usize + self.region.start as usize) as *mut T)
|
|
||||||
.as_mut()
|
|
||||||
.expect("GuestRef implies non-null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Drop for GuestRefMut<'a, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut borrows = self.mem.borrows.borrow_mut();
|
|
||||||
borrows.unborrow_mut(self.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{
|
|
||||||
super::{GuestError, GuestMemory, Region},
|
|
||||||
{GuestPtr, GuestPtrMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(align(4096))]
|
|
||||||
struct HostMemory {
|
|
||||||
buffer: [u8; 4096],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HostMemory {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { buffer: [0; 4096] }
|
|
||||||
}
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
|
||||||
self.buffer.as_mut_ptr()
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn out_of_bounds() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// try extracting an immutable ptr out of memory bounds
|
|
||||||
let err = guest_memory
|
|
||||||
.ptr::<GuestPtr<i32>>(4096)
|
|
||||||
.expect_err("out of bounds ptr error");
|
|
||||||
assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4)));
|
|
||||||
// try extracting an mutable ptr out of memory bounds
|
|
||||||
let err = guest_memory
|
|
||||||
.ptr_mut::<GuestPtrMut<i32>>(4096)
|
|
||||||
.expect_err("out of bounds ptr error");
|
|
||||||
assert_eq!(err, GuestError::PtrOutOfBounds(Region::new(4096, 4)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn not_aligned() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// try extracting a misaligned immutable ptr
|
|
||||||
let err = guest_memory
|
|
||||||
.ptr::<GuestPtr<i32>>(2)
|
|
||||||
.expect_err("ptr misaligned");
|
|
||||||
assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4));
|
|
||||||
// try extracting a misaligned mutable ptr
|
|
||||||
let err = guest_memory
|
|
||||||
.ptr_mut::<GuestPtrMut<i32>>(2)
|
|
||||||
.expect_err("ptr mut misaligned");
|
|
||||||
assert_eq!(err, GuestError::PtrNotAligned(Region::new(2, 4), 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ptr_from_memory() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// write something to memory
|
|
||||||
{
|
|
||||||
let ptr: GuestPtrMut<i64> = guest_memory.ptr_mut(8).expect("ptr mut to the el");
|
|
||||||
let mut el = ptr.as_ref_mut().expect("ref mut to the el");
|
|
||||||
*el = 100;
|
|
||||||
}
|
|
||||||
// extract as ref
|
|
||||||
let ptr: GuestPtr<i64> = guest_memory.ptr(8).expect("ptr to the el");
|
|
||||||
let as_ref = ptr.as_ref().expect("el borrowed immutably");
|
|
||||||
assert_eq!(*as_ref, 100);
|
|
||||||
// borrowing again should be fine
|
|
||||||
let as_ref2 = ptr.as_ref().expect("el borrowed immutably again");
|
|
||||||
assert_eq!(*as_ref2, *as_ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ptr_mut_from_memory() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// set elems of array to zero
|
|
||||||
{
|
|
||||||
let ptr: GuestPtrMut<i64> = guest_memory.ptr_mut(8).expect("ptr mut to the el");
|
|
||||||
let mut el = ptr.as_ref_mut().expect("ref mut to the el");
|
|
||||||
*el = 100;
|
|
||||||
}
|
|
||||||
// extract as ref
|
|
||||||
let ptr: GuestPtrMut<i64> = guest_memory.ptr_mut(8).expect("ptr mut to the el");
|
|
||||||
assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 100);
|
|
||||||
// overwrite the memory and re-verify
|
|
||||||
*ptr.as_ref_mut().expect("el borrowed mutably") = 2000;
|
|
||||||
// re-validate
|
|
||||||
assert_eq!(*ptr.as_ref().expect("el borrowed immutably"), 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(
|
|
||||||
expected = "el borrowed immutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })"
|
|
||||||
)]
|
|
||||||
fn borrow_mut_then_immut() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
let ptr: GuestPtrMut<i16> = guest_memory.ptr_mut(0).expect("ptr mut to the el");
|
|
||||||
// borrow mutably
|
|
||||||
let _as_mut = ptr
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("el borrowed mutably for the first time");
|
|
||||||
// borrow immutably should fail
|
|
||||||
let _as_ref = ptr
|
|
||||||
.as_ref()
|
|
||||||
.expect("el borrowed immutably while borrowed mutably");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(
|
|
||||||
expected = "el borrowed mutably while borrowed mutably: PtrBorrowed(Region { start: 0, len: 2 })"
|
|
||||||
)]
|
|
||||||
fn borrow_mut_twice() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
let ptr: GuestPtrMut<i16> = guest_memory.ptr_mut(0).expect("ptr mut to the el");
|
|
||||||
// borrow mutably
|
|
||||||
let _as_mut = ptr
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("el borrowed mutably for the first time");
|
|
||||||
// try borrowing mutably again
|
|
||||||
let _as_mut2 = ptr
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("el borrowed mutably while borrowed mutably");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
use super::array::{GuestArray, GuestArrayRef};
|
|
||||||
use crate::GuestError;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
pub struct GuestString<'a> {
|
|
||||||
pub(super) array: GuestArray<'a, u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for GuestString<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "GuestString {{ array: {:?} }}", self.array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> GuestString<'a> {
|
|
||||||
pub fn as_ref(&self) -> Result<GuestStringRef<'a>, GuestError> {
|
|
||||||
let ref_ = self.array.as_ref()?;
|
|
||||||
Ok(GuestStringRef { ref_ })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(&self) -> Result<String, GuestError> {
|
|
||||||
Ok(self.as_ref()?.as_str()?.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<GuestArray<'a, u8>> for GuestString<'a> {
|
|
||||||
fn from(array: GuestArray<'a, u8>) -> Self {
|
|
||||||
Self { array }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GuestStringRef<'a> {
|
|
||||||
pub(super) ref_: GuestArrayRef<'a, u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for GuestStringRef<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "GuestStringRef {{ ref_: {:?} }}", self.ref_)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> GuestStringRef<'a> {
|
|
||||||
pub fn as_str(&self) -> Result<&str, GuestError> {
|
|
||||||
std::str::from_utf8(&*self.ref_).map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{
|
|
||||||
super::{
|
|
||||||
ptr::{GuestPtr, GuestPtrMut},
|
|
||||||
GuestError, GuestMemory,
|
|
||||||
},
|
|
||||||
GuestString,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(align(4096))]
|
|
||||||
struct HostMemory {
|
|
||||||
buffer: [u8; 4096],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HostMemory {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { buffer: [0; 4096] }
|
|
||||||
}
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
|
||||||
self.buffer.as_mut_ptr()
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn valid_utf8() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// write string into memory
|
|
||||||
let mut ptr: GuestPtrMut<u8> = guest_memory.ptr_mut(0).expect("ptr mut to start of string");
|
|
||||||
let input_str = "cześć WASI!";
|
|
||||||
for byte in input_str.as_bytes() {
|
|
||||||
let mut ref_mut = ptr.as_ref_mut().expect("valid deref");
|
|
||||||
*ref_mut = *byte;
|
|
||||||
ptr = ptr.elem(1).expect("next ptr");
|
|
||||||
}
|
|
||||||
// read the string as GuestString
|
|
||||||
let ptr: GuestPtr<u8> = guest_memory.ptr(0).expect("ptr to start of string");
|
|
||||||
let guest_string: GuestString<'_> = ptr
|
|
||||||
.array(input_str.len() as u32)
|
|
||||||
.expect("valid null-terminated string")
|
|
||||||
.into();
|
|
||||||
let as_ref = guest_string.as_ref().expect("deref");
|
|
||||||
assert_eq!(as_ref.as_str().expect("valid UTF-8"), input_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_utf8() {
|
|
||||||
let mut host_memory = HostMemory::new();
|
|
||||||
let guest_memory = GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32);
|
|
||||||
// write string into memory
|
|
||||||
let mut ptr: GuestPtrMut<u8> = guest_memory.ptr_mut(0).expect("ptr mut to start of string");
|
|
||||||
let input_str = "cześć WASI!";
|
|
||||||
let mut bytes = input_str.as_bytes().to_vec();
|
|
||||||
// insert 0xFE which is an invalid UTF-8 byte
|
|
||||||
bytes[5] = 0xfe;
|
|
||||||
for byte in &bytes {
|
|
||||||
let mut ref_mut = ptr.as_ref_mut().expect("valid deref");
|
|
||||||
*ref_mut = *byte;
|
|
||||||
ptr = ptr.elem(1).expect("next ptr");
|
|
||||||
}
|
|
||||||
// read the string as GuestString
|
|
||||||
let ptr: GuestPtr<u8> = guest_memory.ptr(0).expect("ptr to start of string");
|
|
||||||
let guest_string: GuestString<'_> = ptr
|
|
||||||
.array(bytes.len() as u32)
|
|
||||||
.expect("valid null-terminated string")
|
|
||||||
.into();
|
|
||||||
let as_ref = guest_string.as_ref().expect("deref");
|
|
||||||
match as_ref.as_str().expect_err("should fail") {
|
|
||||||
GuestError::InvalidUtf8(_) => {}
|
|
||||||
x => assert!(false, "expected GuestError::InvalidUtf8(_), got {:?}", x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
use wiggle_runtime::GuestMemory;
|
use wiggle_runtime::GuestMemory;
|
||||||
|
|
||||||
#[repr(align(4096))]
|
#[repr(align(4096))]
|
||||||
pub struct HostMemory {
|
pub struct HostMemory {
|
||||||
buffer: [u8; 4096],
|
buffer: UnsafeCell<[u8; 4096]>,
|
||||||
}
|
}
|
||||||
impl HostMemory {
|
impl HostMemory {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
HostMemory { buffer: [0; 4096] }
|
HostMemory { buffer: UnsafeCell::new([0; 4096]) }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn guest_memory<'a>(&'a mut self) -> GuestMemory<'a> {
|
|
||||||
GuestMemory::new(self.buffer.as_mut_ptr(), self.buffer.len() as u32)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mem_area_strat(align: u32) -> BoxedStrategy<MemArea> {
|
pub fn mem_area_strat(align: u32) -> BoxedStrategy<MemArea> {
|
||||||
@@ -29,6 +26,15 @@ impl HostMemory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl GuestMemory for HostMemory {
|
||||||
|
fn base(&self) -> (*mut u8, u32) {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.buffer.get();
|
||||||
|
((*ptr).as_mut_ptr(), (*ptr).len() as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MemArea {
|
pub struct MemArea {
|
||||||
pub ptr: u32,
|
pub ptr: u32,
|
||||||
|
|||||||
147
tests/arrays.rs
147
tests/arrays.rs
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestArray, GuestError, GuestPtr, GuestPtrMut, GuestType};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -14,25 +14,25 @@ impl arrays::Arrays for WasiCtx {
|
|||||||
&self,
|
&self,
|
||||||
excuses: &types::ConstExcuseArray,
|
excuses: &types::ConstExcuseArray,
|
||||||
) -> Result<types::Excuse, types::Errno> {
|
) -> Result<types::Excuse, types::Errno> {
|
||||||
let last = GuestType::read(
|
let last = &excuses
|
||||||
&excuses
|
.iter()
|
||||||
.iter()
|
.last()
|
||||||
.last()
|
.expect("input array is non-empty")
|
||||||
.expect("input array is non-empty")
|
.expect("valid ptr to ptr")
|
||||||
.expect("valid ptr to ptr"),
|
.read()
|
||||||
)
|
.expect("valid ptr to some Excuse value");
|
||||||
.expect("valid ptr to some Excuse value");
|
Ok(last.read().expect("dereferencing ptr should succeed"))
|
||||||
Ok(*last.as_ref().expect("dereferencing ptr should succeed"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
|
fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
|
||||||
for excuse in excuses.iter() {
|
for excuse in excuses.iter() {
|
||||||
let ptr_to_ptr = GuestType::read(&excuse.expect("valid ptr to ptr"))
|
let ptr_to_excuse = excuse
|
||||||
|
.expect("valid ptr to ptr")
|
||||||
|
.read()
|
||||||
.expect("valid ptr to some Excuse value");
|
.expect("valid ptr to some Excuse value");
|
||||||
let mut ptr = ptr_to_ptr
|
ptr_to_excuse
|
||||||
.as_ref_mut()
|
.write(types::Excuse::Sleeping)
|
||||||
.expect("dereferencing mut ptr should succeed");
|
.expect("dereferencing mut ptr should succeed");
|
||||||
*ptr = types::Excuse::Sleeping;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,6 @@ struct ReduceExcusesExcercise {
|
|||||||
excuse_values: Vec<types::Excuse>,
|
excuse_values: Vec<types::Excuse>,
|
||||||
excuse_ptr_locs: Vec<MemArea>,
|
excuse_ptr_locs: Vec<MemArea>,
|
||||||
array_ptr_loc: MemArea,
|
array_ptr_loc: MemArea,
|
||||||
array_len_loc: MemArea,
|
|
||||||
return_ptr_loc: MemArea,
|
return_ptr_loc: MemArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,22 +56,18 @@ impl ReduceExcusesExcercise {
|
|||||||
proptest::collection::vec(HostMemory::mem_area_strat(4), 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 * len),
|
||||||
HostMemory::mem_area_strat(4),
|
HostMemory::mem_area_strat(4),
|
||||||
HostMemory::mem_area_strat(4),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.prop_map(
|
.prop_map(
|
||||||
|(excuse_values, excuse_ptr_locs, array_ptr_loc, array_len_loc, return_ptr_loc)| {
|
|(excuse_values, excuse_ptr_locs, array_ptr_loc, return_ptr_loc)| Self {
|
||||||
Self {
|
excuse_values,
|
||||||
excuse_values,
|
excuse_ptr_locs,
|
||||||
excuse_ptr_locs,
|
array_ptr_loc,
|
||||||
array_ptr_loc,
|
return_ptr_loc,
|
||||||
array_len_loc,
|
|
||||||
return_ptr_loc,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.prop_filter("non-overlapping pointers", |e| {
|
.prop_filter("non-overlapping pointers", |e| {
|
||||||
let mut all = vec![&e.array_ptr_loc, &e.array_len_loc, &e.return_ptr_loc];
|
let mut all = vec![&e.array_ptr_loc, &e.return_ptr_loc];
|
||||||
all.extend(e.excuse_ptr_locs.iter());
|
all.extend(e.excuse_ptr_locs.iter());
|
||||||
MemArea::non_overlapping_set(&all)
|
MemArea::non_overlapping_set(&all)
|
||||||
})
|
})
|
||||||
@@ -82,44 +77,31 @@ impl ReduceExcusesExcercise {
|
|||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let mut ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let mut host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
// Populate memory with pointers to generated Excuse values
|
// Populate memory with pointers to generated Excuse values
|
||||||
for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) {
|
for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) {
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(ptr.ptr)
|
.ptr(ptr.ptr)
|
||||||
.expect("ptr mut to Excuse value")
|
.write(excuse)
|
||||||
.as_ref_mut()
|
.expect("deref ptr mut to Excuse value");
|
||||||
.expect("deref ptr mut to Excuse value") = excuse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate array length info
|
|
||||||
*guest_memory
|
|
||||||
.ptr_mut(self.array_len_loc.ptr)
|
|
||||||
.expect("ptr to array len")
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("deref ptr mut to array len") = self.excuse_ptr_locs.len() as u32;
|
|
||||||
|
|
||||||
// Populate the array with pointers to generated Excuse values
|
// Populate the array with pointers to generated Excuse values
|
||||||
{
|
{
|
||||||
let mut next: GuestPtrMut<'_, GuestPtr<types::Excuse>> = guest_memory
|
let array: GuestPtr<'_, [GuestPtr<types::Excuse>]> =
|
||||||
.ptr_mut(self.array_ptr_loc.ptr)
|
host_memory.ptr((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32));
|
||||||
.expect("ptr to array mut");
|
for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) {
|
||||||
for ptr in &self.excuse_ptr_locs {
|
let slot = slot.expect("array should be in bounds");
|
||||||
next.write(
|
slot.write(host_memory.ptr(ptr.ptr))
|
||||||
&guest_memory
|
.expect("should succeed in writing array");
|
||||||
.ptr::<types::Excuse>(ptr.ptr)
|
|
||||||
.expect("ptr to Excuse value"),
|
|
||||||
);
|
|
||||||
next = next.elem(1).expect("increment ptr by 1");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = arrays::reduce_excuses(
|
let res = arrays::reduce_excuses(
|
||||||
&mut ctx,
|
&mut ctx,
|
||||||
&mut guest_memory,
|
&mut host_memory,
|
||||||
self.array_ptr_loc.ptr as i32,
|
self.array_ptr_loc.ptr as i32,
|
||||||
self.array_len_loc.ptr as i32,
|
self.excuse_ptr_locs.len() as i32,
|
||||||
self.return_ptr_loc.ptr as i32,
|
self.return_ptr_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -129,10 +111,9 @@ impl ReduceExcusesExcercise {
|
|||||||
.excuse_values
|
.excuse_values
|
||||||
.last()
|
.last()
|
||||||
.expect("generated vec of excuses should be non-empty");
|
.expect("generated vec of excuses should be non-empty");
|
||||||
let given: types::Excuse = *guest_memory
|
let given: types::Excuse = host_memory
|
||||||
.ptr(self.return_ptr_loc.ptr)
|
.ptr(self.return_ptr_loc.ptr)
|
||||||
.expect("ptr to returned value")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("deref ptr to returned value");
|
.expect("deref ptr to returned value");
|
||||||
assert_eq!(expected, given, "reduce excuses return val");
|
assert_eq!(expected, given, "reduce excuses return val");
|
||||||
}
|
}
|
||||||
@@ -156,7 +137,6 @@ fn excuse_strat() -> impl Strategy<Value = types::Excuse> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PopulateExcusesExcercise {
|
struct PopulateExcusesExcercise {
|
||||||
array_ptr_loc: MemArea,
|
array_ptr_loc: MemArea,
|
||||||
array_len_loc: MemArea,
|
|
||||||
elements: Vec<MemArea>,
|
elements: Vec<MemArea>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,17 +147,15 @@ impl PopulateExcusesExcercise {
|
|||||||
let len_usize = len as usize;
|
let len_usize = len as usize;
|
||||||
(
|
(
|
||||||
HostMemory::mem_area_strat(4 * len),
|
HostMemory::mem_area_strat(4 * len),
|
||||||
HostMemory::mem_area_strat(4),
|
|
||||||
proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
|
proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.prop_map(|(array_ptr_loc, array_len_loc, elements)| Self {
|
.prop_map(|(array_ptr_loc, elements)| Self {
|
||||||
array_ptr_loc,
|
array_ptr_loc,
|
||||||
array_len_loc,
|
|
||||||
elements,
|
elements,
|
||||||
})
|
})
|
||||||
.prop_filter("non-overlapping pointers", |e| {
|
.prop_filter("non-overlapping pointers", |e| {
|
||||||
let mut all = vec![&e.array_ptr_loc, &e.array_len_loc];
|
let mut all = vec![&e.array_ptr_loc];
|
||||||
all.extend(e.elements.iter());
|
all.extend(e.elements.iter());
|
||||||
MemArea::non_overlapping_set(&all)
|
MemArea::non_overlapping_set(&all)
|
||||||
})
|
})
|
||||||
@@ -185,51 +163,38 @@ impl PopulateExcusesExcercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
// Populate array length info
|
|
||||||
*guest_memory
|
|
||||||
.ptr_mut(self.array_len_loc.ptr)
|
|
||||||
.expect("ptr mut to array len")
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("deref ptr mut to array len") = self.elements.len() as u32;
|
|
||||||
|
|
||||||
// Populate array with valid pointers to Excuse type in memory
|
// Populate array with valid pointers to Excuse type in memory
|
||||||
{
|
let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>((
|
||||||
let mut next: GuestPtrMut<'_, GuestPtrMut<types::Excuse>> = guest_memory
|
self.array_ptr_loc.ptr,
|
||||||
.ptr_mut(self.array_ptr_loc.ptr)
|
self.elements.len() as u32,
|
||||||
.expect("ptr mut to the first element of array");
|
));
|
||||||
for ptr in &self.elements {
|
for (ptr, val) in ptr.iter().zip(&self.elements) {
|
||||||
next.write(
|
ptr.expect("should be valid pointer")
|
||||||
&guest_memory
|
.write(host_memory.ptr(val.ptr))
|
||||||
.ptr_mut::<types::Excuse>(ptr.ptr)
|
.expect("failed to write value");
|
||||||
.expect("ptr mut to Excuse value"),
|
|
||||||
);
|
|
||||||
next = next.elem(1).expect("increment ptr by 1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = arrays::populate_excuses(
|
let res = arrays::populate_excuses(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.array_ptr_loc.ptr as i32,
|
self.array_ptr_loc.ptr as i32,
|
||||||
self.array_len_loc.ptr as i32,
|
self.elements.len() as i32,
|
||||||
);
|
);
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno");
|
assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno");
|
||||||
|
|
||||||
let arr: GuestArray<'_, GuestPtr<'_, types::Excuse>> = guest_memory
|
let arr: GuestPtr<'_, [GuestPtr<'_, types::Excuse>]> =
|
||||||
.ptr(self.array_ptr_loc.ptr)
|
host_memory.ptr((self.array_ptr_loc.ptr, self.elements.len() as u32));
|
||||||
.expect("ptr to the first element of array")
|
|
||||||
.array(self.elements.len() as u32)
|
|
||||||
.expect("as array");
|
|
||||||
for el in arr.iter() {
|
for el in arr.iter() {
|
||||||
let ptr_to_ptr = GuestType::read(&el.expect("valid ptr to ptr"))
|
let ptr_to_ptr = el
|
||||||
|
.expect("valid ptr to ptr")
|
||||||
|
.read()
|
||||||
.expect("valid ptr to some Excuse value");
|
.expect("valid ptr to some Excuse value");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*ptr_to_ptr
|
ptr_to_ptr
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("dereferencing ptr to some Excuse value"),
|
.expect("dereferencing ptr to some Excuse value"),
|
||||||
types::Excuse::Sleeping,
|
types::Excuse::Sleeping,
|
||||||
"element should equal Excuse::Sleeping"
|
"element should equal Excuse::Sleeping"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestRef};
|
use wiggle_runtime::{GuestError, GuestMemory};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -29,16 +29,10 @@ struct IntFloatExercise {
|
|||||||
|
|
||||||
impl IntFloatExercise {
|
impl IntFloatExercise {
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let e = atoms::int_float_args(
|
let e = atoms::int_float_args(&ctx, &host_memory, self.an_int as i32, self.an_float);
|
||||||
&mut ctx,
|
|
||||||
&mut guest_memory,
|
|
||||||
self.an_int as i32,
|
|
||||||
self.an_float,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(e, types::Errno::Ok.into(), "int_float_args error");
|
assert_eq!(e, types::Errno::Ok.into(), "int_float_args error");
|
||||||
}
|
}
|
||||||
@@ -64,24 +58,22 @@ struct DoubleIntExercise {
|
|||||||
|
|
||||||
impl DoubleIntExercise {
|
impl DoubleIntExercise {
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let e = atoms::double_int_return_float(
|
let e = atoms::double_int_return_float(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input as i32,
|
self.input as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let return_val: GuestRef<types::AliasToFloat> = guest_memory
|
let return_val = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr::<types::AliasToFloat>(self.return_loc.ptr)
|
||||||
.expect("return loc ptr")
|
.read()
|
||||||
.as_ref()
|
.expect("failed to read return");
|
||||||
.expect("return val ref");
|
|
||||||
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
||||||
assert_eq!(*return_val, (self.input as f32) * 2.0, "return val");
|
assert_eq!(return_val, (self.input as f32) * 2.0, "return val");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strat() -> BoxedStrategy<Self> {
|
pub fn strat() -> BoxedStrategy<Self> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use wiggle_runtime::{GuestError, GuestPtr};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -16,7 +16,7 @@ impl flags::Flags for WasiCtx {
|
|||||||
old_config: types::CarConfig,
|
old_config: types::CarConfig,
|
||||||
other_config_ptr: GuestPtr<types::CarConfig>,
|
other_config_ptr: GuestPtr<types::CarConfig>,
|
||||||
) -> Result<types::CarConfig, types::Errno> {
|
) -> Result<types::CarConfig, types::Errno> {
|
||||||
let other_config = *other_config_ptr.as_ref().map_err(|e| {
|
let other_config = other_config_ptr.read().map_err(|e| {
|
||||||
eprintln!("old_config_ptr error: {}", e);
|
eprintln!("old_config_ptr error: {}", e);
|
||||||
types::Errno::InvalidArg
|
types::Errno::InvalidArg
|
||||||
})?;
|
})?;
|
||||||
@@ -63,30 +63,27 @@ impl ConfigureCarExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
// Populate input ptr
|
// Populate input ptr
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.other_config_by_ptr.ptr)
|
.ptr(self.other_config_by_ptr.ptr)
|
||||||
.expect("ptr mut to CarConfig")
|
.write(self.other_config)
|
||||||
.as_ref_mut()
|
.expect("deref ptr mut to CarConfig");
|
||||||
.expect("deref ptr mut to CarConfig") = self.other_config;
|
|
||||||
|
|
||||||
let res = flags::configure_car(
|
let res = flags::configure_car(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.old_config.into(),
|
self.old_config.into(),
|
||||||
self.other_config_by_ptr.ptr as i32,
|
self.other_config_by_ptr.ptr as i32,
|
||||||
self.return_ptr_loc.ptr as i32,
|
self.return_ptr_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "configure car errno");
|
assert_eq!(res, types::Errno::Ok.into(), "configure car errno");
|
||||||
|
|
||||||
let res_config = *guest_memory
|
let res_config = host_memory
|
||||||
.ptr::<types::CarConfig>(self.return_ptr_loc.ptr)
|
.ptr::<types::CarConfig>(self.return_ptr_loc.ptr)
|
||||||
.expect("ptr to returned CarConfig")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("deref to CarConfig value");
|
.expect("deref to CarConfig value");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestType};
|
use wiggle_runtime::{GuestError, GuestType, GuestMemory};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
const FD_VAL: u32 = 123;
|
const FD_VAL: u32 = 123;
|
||||||
@@ -32,27 +32,25 @@ struct HandleExercise {
|
|||||||
|
|
||||||
impl HandleExercise {
|
impl HandleExercise {
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let e = handle_examples::fd_create(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32);
|
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");
|
assert_eq!(e, types::Errno::Ok.into(), "fd_create error");
|
||||||
|
|
||||||
let h_got: u32 = *guest_memory
|
let h_got: u32 = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref_mut");
|
.expect("return ref_mut");
|
||||||
|
|
||||||
assert_eq!(h_got, 123, "fd_create return val");
|
assert_eq!(h_got, 123, "fd_create return val");
|
||||||
|
|
||||||
let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32);
|
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32);
|
||||||
|
|
||||||
assert_eq!(e, types::Errno::Ok.into(), "fd_consume error");
|
assert_eq!(e, types::Errno::Ok.into(), "fd_consume error");
|
||||||
|
|
||||||
let e = handle_examples::fd_consume(&mut ctx, &mut guest_memory, h_got as i32 + 1);
|
let e = handle_examples::fd_consume(&ctx, &host_memory, h_got as i32 + 1);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
e,
|
e,
|
||||||
@@ -62,7 +60,7 @@ impl HandleExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn strat() -> BoxedStrategy<Self> {
|
pub fn strat() -> BoxedStrategy<Self> {
|
||||||
(HostMemory::mem_area_strat(types::Fd::size()))
|
(HostMemory::mem_area_strat(types::Fd::guest_size()))
|
||||||
.prop_map(|return_loc| HandleExercise { return_loc })
|
.prop_map(|return_loc| HandleExercise { return_loc })
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use wiggle_runtime::GuestError;
|
use wiggle_runtime::{GuestError, GuestMemory};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -44,22 +44,20 @@ impl CookieCutterExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let res = ints::cookie_cutter(
|
let res = ints::cookie_cutter(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.cookie.into(),
|
self.cookie.into(),
|
||||||
self.return_ptr_loc.ptr as i32,
|
self.return_ptr_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno");
|
assert_eq!(res, types::Errno::Ok.into(), "cookie cutter errno");
|
||||||
|
|
||||||
let is_cookie_start = *guest_memory
|
let is_cookie_start = host_memory
|
||||||
.ptr::<types::Bool>(self.return_ptr_loc.ptr)
|
.ptr::<types::Bool>(self.return_ptr_loc.ptr)
|
||||||
.expect("ptr to returned Bool")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("deref to Bool value");
|
.expect("deref to Bool value");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestPtr, GuestPtrMut, GuestRefMut, GuestType};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -10,49 +10,52 @@ wiggle::from_witx!({
|
|||||||
impl_errno!(types::Errno);
|
impl_errno!(types::Errno);
|
||||||
|
|
||||||
impl pointers::Pointers for WasiCtx {
|
impl pointers::Pointers for WasiCtx {
|
||||||
fn pointers_and_enums(
|
fn pointers_and_enums<'a>(
|
||||||
&self,
|
&self,
|
||||||
input1: types::Excuse,
|
input1: types::Excuse,
|
||||||
input2_ptr: GuestPtrMut<types::Excuse>,
|
input2_ptr: GuestPtr<'a, types::Excuse>,
|
||||||
input3_ptr: GuestPtr<types::Excuse>,
|
input3_ptr: GuestPtr<'a, types::Excuse>,
|
||||||
input4_ptr_ptr: GuestPtrMut<GuestPtr<types::Excuse>>,
|
input4_ptr_ptr: GuestPtr<'a, GuestPtr<'a, types::Excuse>>,
|
||||||
) -> Result<(), types::Errno> {
|
) -> Result<(), types::Errno> {
|
||||||
println!("BAZ input1 {:?}", input1);
|
println!("BAZ input1 {:?}", input1);
|
||||||
// Read enum value from mutable:
|
let input2: types::Excuse = input2_ptr.read().map_err(|e| {
|
||||||
let mut input2_ref: GuestRefMut<types::Excuse> = input2_ptr.as_ref_mut().map_err(|e| {
|
|
||||||
eprintln!("input2_ptr error: {}", e);
|
eprintln!("input2_ptr error: {}", e);
|
||||||
types::Errno::InvalidArg
|
types::Errno::InvalidArg
|
||||||
})?;
|
})?;
|
||||||
let input2: types::Excuse = *input2_ref;
|
|
||||||
println!("input2 {:?}", input2);
|
println!("input2 {:?}", input2);
|
||||||
|
|
||||||
// Read enum value from immutable ptr:
|
// Read enum value from immutable ptr:
|
||||||
let input3 = *input3_ptr.as_ref().map_err(|e| {
|
let input3 = input3_ptr.read().map_err(|e| {
|
||||||
eprintln!("input3_ptr error: {}", e);
|
eprintln!("input3_ptr error: {}", e);
|
||||||
types::Errno::InvalidArg
|
types::Errno::InvalidArg
|
||||||
})?;
|
})?;
|
||||||
println!("input3 {:?}", input3);
|
println!("input3 {:?}", input3);
|
||||||
|
|
||||||
// Write enum to mutable ptr:
|
// Write enum to mutable ptr:
|
||||||
*input2_ref = input3;
|
input2_ptr.write(input3).map_err(|e| {
|
||||||
|
eprintln!("input2_ptr error: {}", e);
|
||||||
|
types::Errno::InvalidArg
|
||||||
|
})?;
|
||||||
println!("wrote to input2_ref {:?}", input3);
|
println!("wrote to input2_ref {:?}", input3);
|
||||||
|
|
||||||
// Read ptr value from mutable ptr:
|
// Read ptr value from mutable ptr:
|
||||||
let input4_ptr: GuestPtr<types::Excuse> = GuestType::read(&input4_ptr_ptr.as_immut())
|
let input4_ptr: GuestPtr<types::Excuse> = input4_ptr_ptr.read().map_err(|e| {
|
||||||
.map_err(|e| {
|
eprintln!("input4_ptr_ptr error: {}", e);
|
||||||
eprintln!("input4_ptr_ptr error: {}", e);
|
types::Errno::InvalidArg
|
||||||
types::Errno::InvalidArg
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Read enum value from that ptr:
|
// Read enum value from that ptr:
|
||||||
let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| {
|
let input4: types::Excuse = input4_ptr.read().map_err(|e| {
|
||||||
eprintln!("input4_ptr error: {}", e);
|
eprintln!("input4_ptr error: {}", e);
|
||||||
types::Errno::InvalidArg
|
types::Errno::InvalidArg
|
||||||
})?;
|
})?;
|
||||||
println!("input4 {:?}", input4);
|
println!("input4 {:?}", input4);
|
||||||
|
|
||||||
// Write ptr value to mutable ptr:
|
// Write ptr value to mutable ptr:
|
||||||
input4_ptr_ptr.write(&input2_ptr.as_immut());
|
input4_ptr_ptr.write(input2_ptr).map_err(|e| {
|
||||||
|
eprintln!("input4_ptr_ptr error: {}", e);
|
||||||
|
types::Errno::InvalidArg
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -123,37 +126,32 @@ impl PointersAndEnumsExercise {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input2_loc.ptr)
|
.ptr(self.input2_loc.ptr)
|
||||||
.expect("input2 ptr")
|
.write(self.input2)
|
||||||
.as_ref_mut()
|
.expect("input2 ref_mut");
|
||||||
.expect("input2 ref_mut") = self.input2;
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input3_loc.ptr)
|
.ptr(self.input3_loc.ptr)
|
||||||
.expect("input3 ptr")
|
.write(self.input3)
|
||||||
.as_ref_mut()
|
.expect("input3 ref_mut");
|
||||||
.expect("input3 ref_mut") = self.input3;
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input4_loc.ptr)
|
.ptr(self.input4_loc.ptr)
|
||||||
.expect("input4 ptr")
|
.write(self.input4)
|
||||||
.as_ref_mut()
|
.expect("input4 ref_mut");
|
||||||
.expect("input4 ref_mut") = self.input4;
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input4_ptr_loc.ptr)
|
.ptr(self.input4_ptr_loc.ptr)
|
||||||
.expect("input4 ptr ptr")
|
.write(self.input4_loc.ptr)
|
||||||
.as_ref_mut()
|
.expect("input4 ptr ref_mut");
|
||||||
.expect("input4 ptr ref_mut") = self.input4_loc.ptr;
|
|
||||||
|
|
||||||
let e = pointers::pointers_and_enums(
|
let e = pointers::pointers_and_enums(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input1.into(),
|
self.input1.into(),
|
||||||
self.input2_loc.ptr as i32,
|
self.input2_loc.ptr as i32,
|
||||||
self.input3_loc.ptr as i32,
|
self.input3_loc.ptr as i32,
|
||||||
@@ -162,10 +160,9 @@ impl PointersAndEnumsExercise {
|
|||||||
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
assert_eq!(e, types::Errno::Ok.into(), "errno");
|
||||||
|
|
||||||
// Implementation of pointers_and_enums writes input3 to the input2_loc:
|
// Implementation of pointers_and_enums writes input3 to the input2_loc:
|
||||||
let written_to_input2_loc: i32 = *guest_memory
|
let written_to_input2_loc: i32 = host_memory
|
||||||
.ptr(self.input2_loc.ptr)
|
.ptr(self.input2_loc.ptr)
|
||||||
.expect("input2 ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("input2 ref");
|
.expect("input2 ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -175,10 +172,9 @@ impl PointersAndEnumsExercise {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc:
|
// Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc:
|
||||||
let written_to_input4_ptr: u32 = *guest_memory
|
let written_to_input4_ptr: u32 = host_memory
|
||||||
.ptr(self.input4_ptr_loc.ptr)
|
.ptr(self.input4_ptr_loc.ptr)
|
||||||
.expect("input4_ptr_loc ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("input4_ptr_loc ref");
|
.expect("input4_ptr_loc ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestPtrMut, GuestString};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -10,11 +10,12 @@ wiggle::from_witx!({
|
|||||||
impl_errno!(types::Errno);
|
impl_errno!(types::Errno);
|
||||||
|
|
||||||
impl strings::Strings for WasiCtx {
|
impl strings::Strings for WasiCtx {
|
||||||
fn hello_string(&self, a_string: &GuestString<'_>) -> Result<u32, types::Errno> {
|
fn hello_string(&self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> {
|
||||||
let as_ref = a_string.as_ref().expect("deref ptr should succeed");
|
let s = a_string.as_raw().expect("should be valid string");
|
||||||
let as_str = as_ref.as_str().expect("valid UTF-8 string");
|
unsafe {
|
||||||
println!("a_string='{}'", as_str);
|
println!("a_string='{}'", &*s);
|
||||||
Ok(as_str.len() as u32)
|
Ok((*s).len() as u32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +27,6 @@ fn test_string_strategy() -> impl Strategy<Value = String> {
|
|||||||
struct HelloStringExercise {
|
struct HelloStringExercise {
|
||||||
test_word: String,
|
test_word: String,
|
||||||
string_ptr_loc: MemArea,
|
string_ptr_loc: MemArea,
|
||||||
string_len_loc: MemArea,
|
|
||||||
return_ptr_loc: MemArea,
|
return_ptr_loc: MemArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,63 +38,43 @@ impl HelloStringExercise {
|
|||||||
Just(test_word.clone()),
|
Just(test_word.clone()),
|
||||||
HostMemory::mem_area_strat(test_word.len() as u32),
|
HostMemory::mem_area_strat(test_word.len() as u32),
|
||||||
HostMemory::mem_area_strat(4),
|
HostMemory::mem_area_strat(4),
|
||||||
HostMemory::mem_area_strat(4),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.prop_map(
|
.prop_map(|(test_word, string_ptr_loc, return_ptr_loc)| Self {
|
||||||
|(test_word, string_ptr_loc, string_len_loc, return_ptr_loc)| Self {
|
test_word,
|
||||||
test_word,
|
string_ptr_loc,
|
||||||
string_ptr_loc,
|
return_ptr_loc,
|
||||||
string_len_loc,
|
})
|
||||||
return_ptr_loc,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.prop_filter("non-overlapping pointers", |e| {
|
.prop_filter("non-overlapping pointers", |e| {
|
||||||
MemArea::non_overlapping_set(&[
|
MemArea::non_overlapping_set(&[&e.string_ptr_loc, &e.return_ptr_loc])
|
||||||
&e.string_ptr_loc,
|
|
||||||
&e.string_len_loc,
|
|
||||||
&e.return_ptr_loc,
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
// Populate string length
|
|
||||||
*guest_memory
|
|
||||||
.ptr_mut(self.string_len_loc.ptr)
|
|
||||||
.expect("ptr mut to string len")
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("deref ptr mut to string len") = self.test_word.len() as u32;
|
|
||||||
|
|
||||||
// Populate string in guest's memory
|
// Populate string in guest's memory
|
||||||
{
|
let ptr = host_memory.ptr::<str>((self.string_ptr_loc.ptr, self.test_word.len() as u32));
|
||||||
let mut next: GuestPtrMut<'_, u8> = guest_memory
|
for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) {
|
||||||
.ptr_mut(self.string_ptr_loc.ptr)
|
slot.expect("should be valid pointer")
|
||||||
.expect("ptr mut to the first byte of string");
|
.write(byte)
|
||||||
for byte in self.test_word.as_bytes() {
|
.expect("failed to write");
|
||||||
*next.as_ref_mut().expect("deref mut") = *byte;
|
|
||||||
next = next.elem(1).expect("increment ptr by 1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = strings::hello_string(
|
let res = strings::hello_string(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.string_ptr_loc.ptr as i32,
|
self.string_ptr_loc.ptr as i32,
|
||||||
self.string_len_loc.ptr as i32,
|
self.test_word.len() as i32,
|
||||||
self.return_ptr_loc.ptr as i32,
|
self.return_ptr_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "hello string errno");
|
assert_eq!(res, types::Errno::Ok.into(), "hello string errno");
|
||||||
|
|
||||||
let given = *guest_memory
|
let given = host_memory
|
||||||
.ptr::<u32>(self.return_ptr_loc.ptr)
|
.ptr::<u32>(self.return_ptr_loc.ptr)
|
||||||
.expect("ptr to return value")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("deref ptr to return value");
|
.expect("deref ptr to return value");
|
||||||
assert_eq!(self.test_word.len() as u32, given);
|
assert_eq!(self.test_word.len() as u32, given);
|
||||||
}
|
}
|
||||||
|
|||||||
201
tests/structs.rs
201
tests/structs.rs
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestPtr};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestPtr};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -15,21 +15,21 @@ impl structs::Structs for WasiCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result<i64, types::Errno> {
|
fn sum_of_pair_of_ptrs(&self, an_pair: &types::PairIntPtrs) -> Result<i64, types::Errno> {
|
||||||
let first = *an_pair
|
let first = an_pair
|
||||||
.first
|
.first
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("dereferencing GuestPtr should succeed");
|
.expect("dereferencing GuestPtr should succeed");
|
||||||
let second = *an_pair
|
let second = an_pair
|
||||||
.second
|
.second
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("dereferncing GuestPtr should succeed");
|
.expect("dereferncing GuestPtr should succeed");
|
||||||
Ok(first as i64 + second as i64)
|
Ok(first as i64 + second as i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result<i64, types::Errno> {
|
fn sum_of_int_and_ptr(&self, an_pair: &types::PairIntAndPtr) -> Result<i64, types::Errno> {
|
||||||
let first = *an_pair
|
let first = an_pair
|
||||||
.first
|
.first
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("dereferencing GuestPtr should succeed");
|
.expect("dereferencing GuestPtr should succeed");
|
||||||
let second = an_pair.second as i64;
|
let second = an_pair.second as i64;
|
||||||
Ok(first as i64 + second)
|
Ok(first as i64 + second)
|
||||||
@@ -78,33 +78,29 @@ impl SumOfPairExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_loc.ptr)
|
.ptr(self.input_loc.ptr)
|
||||||
.expect("input ptr")
|
.write(self.input.first)
|
||||||
.as_ref_mut()
|
.expect("input ref_mut");
|
||||||
.expect("input ref_mut") = self.input.first;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_loc.ptr + 4)
|
||||||
.ptr_mut(self.input_loc.ptr + 4)
|
.write(self.input.second)
|
||||||
.expect("input ptr")
|
.expect("input ref_mut");
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input ref_mut") = self.input.second;
|
|
||||||
let sum_err = structs::sum_of_pair(
|
let sum_err = structs::sum_of_pair(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_loc.ptr as i32,
|
self.input_loc.ptr as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno");
|
assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno");
|
||||||
|
|
||||||
let return_val: i64 = *guest_memory
|
let return_val: i64 = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref");
|
.expect("return ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -170,45 +166,39 @@ impl SumPairPtrsExercise {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_first_loc.ptr)
|
.ptr(self.input_first_loc.ptr)
|
||||||
.expect("input_first ptr")
|
.write(self.input_first)
|
||||||
.as_ref_mut()
|
.expect("input_first ref");
|
||||||
.expect("input_first ref") = self.input_first;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_second_loc.ptr)
|
||||||
.ptr_mut(self.input_second_loc.ptr)
|
.write(self.input_second)
|
||||||
.expect("input_second ptr")
|
.expect("input_second ref");
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input_second ref") = self.input_second;
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_struct_loc.ptr)
|
.ptr(self.input_struct_loc.ptr)
|
||||||
.expect("input_struct ptr")
|
.write(self.input_first_loc.ptr)
|
||||||
.as_ref_mut()
|
.expect("input_struct ref");
|
||||||
.expect("input_struct ref") = self.input_first_loc.ptr;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_struct_loc.ptr + 4)
|
||||||
.ptr_mut(self.input_struct_loc.ptr + 4)
|
.write(self.input_second_loc.ptr)
|
||||||
.expect("input_struct ptr")
|
.expect("input_struct ref");
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input_struct ref") = self.input_second_loc.ptr;
|
|
||||||
|
|
||||||
let res = structs::sum_of_pair_of_ptrs(
|
let res = structs::sum_of_pair_of_ptrs(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_struct_loc.ptr as i32,
|
self.input_struct_loc.ptr as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno");
|
assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno");
|
||||||
|
|
||||||
let doubled: i64 = *guest_memory
|
let doubled: i64 = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref");
|
.expect("return ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -264,39 +254,34 @@ impl SumIntAndPtrExercise {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_first_loc.ptr)
|
.ptr(self.input_first_loc.ptr)
|
||||||
.expect("input_first ptr")
|
.write(self.input_first)
|
||||||
.as_ref_mut()
|
.expect("input_first ref");
|
||||||
.expect("input_first ref") = self.input_first;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_struct_loc.ptr)
|
||||||
.ptr_mut(self.input_struct_loc.ptr)
|
.write(self.input_first_loc.ptr)
|
||||||
.expect("input_struct ptr")
|
.expect("input_struct ref");
|
||||||
.as_ref_mut()
|
host_memory
|
||||||
.expect("input_struct ref") = self.input_first_loc.ptr;
|
.ptr(self.input_struct_loc.ptr + 4)
|
||||||
*guest_memory
|
.write(self.input_second)
|
||||||
.ptr_mut(self.input_struct_loc.ptr + 4)
|
.expect("input_struct ref");
|
||||||
.expect("input_struct ptr")
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input_struct ref") = self.input_second;
|
|
||||||
|
|
||||||
let res = structs::sum_of_int_and_ptr(
|
let res = structs::sum_of_int_and_ptr(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_struct_loc.ptr as i32,
|
self.input_struct_loc.ptr as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "sum of int and ptr errno");
|
assert_eq!(res, types::Errno::Ok.into(), "sum of int and ptr errno");
|
||||||
|
|
||||||
let doubled: i64 = *guest_memory
|
let doubled: i64 = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref");
|
.expect("return ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -326,19 +311,16 @@ impl ReturnPairInts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let err =
|
let err = structs::return_pair_ints(&ctx, &host_memory, self.return_loc.ptr as i32);
|
||||||
structs::return_pair_ints(&mut ctx, &mut guest_memory, self.return_loc.ptr as i32);
|
|
||||||
|
|
||||||
assert_eq!(err, types::Errno::Ok.into(), "return struct errno");
|
assert_eq!(err, types::Errno::Ok.into(), "return struct errno");
|
||||||
|
|
||||||
let return_struct: types::PairInts = *guest_memory
|
let return_struct: types::PairInts = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref");
|
.expect("return ref");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -398,24 +380,21 @@ impl ReturnPairPtrsExercise {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_first_loc.ptr)
|
.ptr(self.input_first_loc.ptr)
|
||||||
.expect("input_first ptr")
|
.write(self.input_first)
|
||||||
.as_ref_mut()
|
.expect("input_first ref");
|
||||||
.expect("input_first ref") = self.input_first;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_second_loc.ptr)
|
||||||
.ptr_mut(self.input_second_loc.ptr)
|
.write(self.input_second)
|
||||||
.expect("input_second ptr")
|
.expect("input_second ref");
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input_second ref") = self.input_second;
|
|
||||||
|
|
||||||
let res = structs::return_pair_of_ptrs(
|
let res = structs::return_pair_of_ptrs(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_first_loc.ptr as i32,
|
self.input_first_loc.ptr as i32,
|
||||||
self.input_second_loc.ptr as i32,
|
self.input_second_loc.ptr as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
@@ -423,28 +402,20 @@ impl ReturnPairPtrsExercise {
|
|||||||
|
|
||||||
assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno");
|
assert_eq!(res, types::Errno::Ok.into(), "return pair of ptrs errno");
|
||||||
|
|
||||||
let ptr_pair_int_ptrs: GuestPtr<types::PairIntPtrs<'_>> =
|
let ptr_pair_int_ptrs: types::PairIntPtrs<'_> =
|
||||||
guest_memory.ptr(self.return_loc.ptr).expect("return ptr");
|
host_memory.ptr(self.return_loc.ptr).read().expect("failed to read return location");
|
||||||
let ret_first_ptr: GuestPtr<i32> = ptr_pair_int_ptrs
|
let ret_first_ptr = ptr_pair_int_ptrs.first;
|
||||||
.cast::<GuestPtr<i32>>(0u32)
|
let ret_second_ptr = ptr_pair_int_ptrs.second;
|
||||||
.expect("extract ptr to first element in struct")
|
|
||||||
.read()
|
|
||||||
.expect("read ptr to first element in struct");
|
|
||||||
let ret_second_ptr: GuestPtr<i32> = ptr_pair_int_ptrs
|
|
||||||
.cast::<GuestPtr<i32>>(4u32)
|
|
||||||
.expect("extract ptr to second element in struct")
|
|
||||||
.read()
|
|
||||||
.expect("read ptr to second element in struct");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.input_first,
|
self.input_first,
|
||||||
*ret_first_ptr
|
ret_first_ptr
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("deref extracted ptr to first element")
|
.expect("deref extracted ptr to first element")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
self.input_second,
|
self.input_second,
|
||||||
*ret_second_ptr
|
ret_second_ptr
|
||||||
.as_ref()
|
.read()
|
||||||
.expect("deref extracted ptr to second element")
|
.expect("deref extracted ptr to second element")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
118
tests/union.rs
118
tests/union.rs
@@ -1,5 +1,5 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use wiggle_runtime::{GuestError, GuestType};
|
use wiggle_runtime::{GuestError, GuestMemory, GuestType};
|
||||||
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -43,16 +43,16 @@ impl union_example::UnionExample for WasiCtx {
|
|||||||
fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> {
|
fn reason_mult(&self, u: &types::ReasonMut<'_>, multiply_by: u32) -> Result<(), types::Errno> {
|
||||||
match u {
|
match u {
|
||||||
types::ReasonMut::DogAte(fptr) => {
|
types::ReasonMut::DogAte(fptr) => {
|
||||||
let mut f = fptr.as_ref_mut().expect("valid pointer");
|
let val = fptr.read().expect("valid pointer");
|
||||||
let val = *f;
|
|
||||||
println!("REASON MULT DogAte({})", val);
|
println!("REASON MULT DogAte({})", val);
|
||||||
*f = mult_zero_nan(val, multiply_by);
|
fptr.write(mult_zero_nan(val, multiply_by))
|
||||||
|
.expect("valid pointer");
|
||||||
}
|
}
|
||||||
types::ReasonMut::Traffic(iptr) => {
|
types::ReasonMut::Traffic(iptr) => {
|
||||||
let mut i = iptr.as_ref_mut().expect("valid pointer");
|
let val = iptr.read().expect("valid pointer");
|
||||||
let val: i32 = *i;
|
|
||||||
println!("REASON MULT Traffic({})", val);
|
println!("REASON MULT Traffic({})", val);
|
||||||
*i = mult_lose_overflow(val, multiply_by);
|
iptr.write(mult_lose_overflow(val, multiply_by))
|
||||||
|
.expect("valid pointer");
|
||||||
}
|
}
|
||||||
types::ReasonMut::Sleeping => {
|
types::ReasonMut::Sleeping => {
|
||||||
println!("REASON MULT Sleeping");
|
println!("REASON MULT Sleeping");
|
||||||
@@ -90,8 +90,8 @@ impl GetTagExercise {
|
|||||||
pub fn strat() -> BoxedStrategy<Self> {
|
pub fn strat() -> BoxedStrategy<Self> {
|
||||||
(
|
(
|
||||||
reason_strat(),
|
reason_strat(),
|
||||||
HostMemory::mem_area_strat(types::Reason::size()),
|
HostMemory::mem_area_strat(types::Reason::guest_size()),
|
||||||
HostMemory::mem_area_strat(types::Excuse::size()),
|
HostMemory::mem_area_strat(types::Excuse::guest_size()),
|
||||||
)
|
)
|
||||||
.prop_map(|(input, input_loc, return_loc)| GetTagExercise {
|
.prop_map(|(input, input_loc, return_loc)| GetTagExercise {
|
||||||
input,
|
input,
|
||||||
@@ -105,46 +105,39 @@ impl GetTagExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let discriminant: u8 = reason_tag(&self.input).into();
|
let discriminant: u8 = reason_tag(&self.input).into();
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_loc.ptr)
|
.ptr(self.input_loc.ptr)
|
||||||
.expect("input discriminant ptr")
|
.write(discriminant)
|
||||||
.as_ref_mut()
|
.expect("input discriminant ptr");
|
||||||
.expect("input discriminant ref_mut") = discriminant;
|
|
||||||
match self.input {
|
match self.input {
|
||||||
types::Reason::DogAte(f) => {
|
types::Reason::DogAte(f) => {
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_loc.ptr + 4)
|
.ptr(self.input_loc.ptr + 4)
|
||||||
.expect("input contents ptr")
|
.write(f)
|
||||||
.as_ref_mut()
|
.expect("input contents ref_mut");
|
||||||
.expect("input contents ref_mut") = f;
|
|
||||||
}
|
|
||||||
types::Reason::Traffic(v) => {
|
|
||||||
*guest_memory
|
|
||||||
.ptr_mut(self.input_loc.ptr + 4)
|
|
||||||
.expect("input contents ptr")
|
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input contents ref_mut") = v;
|
|
||||||
}
|
}
|
||||||
|
types::Reason::Traffic(v) => host_memory
|
||||||
|
.ptr(self.input_loc.ptr + 4)
|
||||||
|
.write(v)
|
||||||
|
.expect("input contents ref_mut"),
|
||||||
types::Reason::Sleeping => {} // Do nothing
|
types::Reason::Sleeping => {} // Do nothing
|
||||||
}
|
}
|
||||||
let e = union_example::get_tag(
|
let e = union_example::get_tag(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_loc.ptr as i32,
|
self.input_loc.ptr as i32,
|
||||||
self.return_loc.ptr as i32,
|
self.return_loc.ptr as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(e, types::Errno::Ok.into(), "get_tag errno");
|
assert_eq!(e, types::Errno::Ok.into(), "get_tag errno");
|
||||||
|
|
||||||
let return_val: types::Excuse = *guest_memory
|
let return_val: types::Excuse = host_memory
|
||||||
.ptr(self.return_loc.ptr)
|
.ptr(self.return_loc.ptr)
|
||||||
.expect("return ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("return ref");
|
.expect("return ref");
|
||||||
|
|
||||||
assert_eq!(return_val, reason_tag(&self.input), "get_tag return value");
|
assert_eq!(return_val, reason_tag(&self.input), "get_tag return value");
|
||||||
@@ -170,7 +163,7 @@ impl ReasonMultExercise {
|
|||||||
pub fn strat() -> BoxedStrategy<Self> {
|
pub fn strat() -> BoxedStrategy<Self> {
|
||||||
(
|
(
|
||||||
reason_strat(),
|
reason_strat(),
|
||||||
HostMemory::mem_area_strat(types::Reason::size()),
|
HostMemory::mem_area_strat(types::Reason::guest_size()),
|
||||||
HostMemory::mem_area_strat(4),
|
HostMemory::mem_area_strat(4),
|
||||||
prop::num::u32::ANY,
|
prop::num::u32::ANY,
|
||||||
)
|
)
|
||||||
@@ -189,42 +182,37 @@ impl ReasonMultExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&self) {
|
pub fn test(&self) {
|
||||||
let mut ctx = WasiCtx::new();
|
let ctx = WasiCtx::new();
|
||||||
let mut host_memory = HostMemory::new();
|
let host_memory = HostMemory::new();
|
||||||
let mut guest_memory = host_memory.guest_memory();
|
|
||||||
|
|
||||||
let discriminant: u8 = reason_tag(&self.input).into();
|
let discriminant: u8 = reason_tag(&self.input).into();
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_loc.ptr)
|
.ptr(self.input_loc.ptr)
|
||||||
.expect("input discriminant ptr")
|
.write(discriminant)
|
||||||
.as_ref_mut()
|
.expect("input discriminant ref_mut");
|
||||||
.expect("input discriminant ref_mut") = discriminant;
|
host_memory
|
||||||
*guest_memory
|
.ptr(self.input_loc.ptr + 4)
|
||||||
.ptr_mut(self.input_loc.ptr + 4)
|
.write(self.input_pointee_loc.ptr)
|
||||||
.expect("input pointer ptr")
|
.expect("input pointer ref_mut");
|
||||||
.as_ref_mut()
|
|
||||||
.expect("input pointer ref_mut") = self.input_pointee_loc.ptr;
|
|
||||||
|
|
||||||
match self.input {
|
match self.input {
|
||||||
types::Reason::DogAte(f) => {
|
types::Reason::DogAte(f) => {
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_pointee_loc.ptr)
|
.ptr(self.input_pointee_loc.ptr)
|
||||||
.expect("input contents ptr")
|
.write(f)
|
||||||
.as_ref_mut()
|
.expect("input contents ref_mut");
|
||||||
.expect("input contents ref_mut") = f;
|
|
||||||
}
|
}
|
||||||
types::Reason::Traffic(v) => {
|
types::Reason::Traffic(v) => {
|
||||||
*guest_memory
|
host_memory
|
||||||
.ptr_mut(self.input_pointee_loc.ptr)
|
.ptr(self.input_pointee_loc.ptr)
|
||||||
.expect("input contents ptr")
|
.write(v)
|
||||||
.as_ref_mut()
|
.expect("input contents ref_mut");
|
||||||
.expect("input contents ref_mut") = v;
|
|
||||||
}
|
}
|
||||||
types::Reason::Sleeping => {} // Do nothing
|
types::Reason::Sleeping => {} // Do nothing
|
||||||
}
|
}
|
||||||
let e = union_example::reason_mult(
|
let e = union_example::reason_mult(
|
||||||
&mut ctx,
|
&ctx,
|
||||||
&mut guest_memory,
|
&host_memory,
|
||||||
self.input_loc.ptr as i32,
|
self.input_loc.ptr as i32,
|
||||||
self.multiply_by as i32,
|
self.multiply_by as i32,
|
||||||
);
|
);
|
||||||
@@ -233,10 +221,9 @@ impl ReasonMultExercise {
|
|||||||
|
|
||||||
match self.input {
|
match self.input {
|
||||||
types::Reason::DogAte(f) => {
|
types::Reason::DogAte(f) => {
|
||||||
let f_result: f32 = *guest_memory
|
let f_result: f32 = host_memory
|
||||||
.ptr(self.input_pointee_loc.ptr)
|
.ptr(self.input_pointee_loc.ptr)
|
||||||
.expect("input contents ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("input contents ref_mut");
|
.expect("input contents ref_mut");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mult_zero_nan(f, self.multiply_by),
|
mult_zero_nan(f, self.multiply_by),
|
||||||
@@ -245,10 +232,9 @@ impl ReasonMultExercise {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
types::Reason::Traffic(v) => {
|
types::Reason::Traffic(v) => {
|
||||||
let v_result: i32 = *guest_memory
|
let v_result: i32 = host_memory
|
||||||
.ptr(self.input_pointee_loc.ptr)
|
.ptr(self.input_pointee_loc.ptr)
|
||||||
.expect("input contents ptr")
|
.read()
|
||||||
.as_ref()
|
|
||||||
.expect("input contents ref_mut");
|
.expect("input contents ref_mut");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mult_lose_overflow(v, self.multiply_by),
|
mult_lose_overflow(v, self.multiply_by),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr, GuestPtrMut, GuestString};
|
use wiggle_runtime::{GuestError, GuestErrorType, GuestPtr};
|
||||||
use wiggle_test::WasiCtx;
|
use wiggle_test::WasiCtx;
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -25,8 +25,8 @@ impl GuestErrorType for types::Errno {
|
|||||||
impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
||||||
fn args_get(
|
fn args_get(
|
||||||
&self,
|
&self,
|
||||||
_argv: GuestPtrMut<GuestPtrMut<u8>>,
|
_argv: GuestPtr<GuestPtr<u8>>,
|
||||||
_argv_buf: GuestPtrMut<u8>,
|
_argv_buf: GuestPtr<u8>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("args_get")
|
unimplemented!("args_get")
|
||||||
}
|
}
|
||||||
@@ -37,8 +37,8 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
|
|
||||||
fn environ_get(
|
fn environ_get(
|
||||||
&self,
|
&self,
|
||||||
_environ: GuestPtrMut<GuestPtrMut<u8>>,
|
_environ: GuestPtr<GuestPtr<u8>>,
|
||||||
_environ_buf: GuestPtrMut<u8>,
|
_environ_buf: GuestPtr<u8>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("environ_get")
|
unimplemented!("environ_get")
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
fn fd_prestat_dir_name(
|
fn fd_prestat_dir_name(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_path: GuestPtrMut<u8>,
|
_path: GuestPtr<u8>,
|
||||||
_path_len: types::Size,
|
_path_len: types::Size,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("fd_prestat_dir_name")
|
unimplemented!("fd_prestat_dir_name")
|
||||||
@@ -159,7 +159,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
fn fd_readdir(
|
fn fd_readdir(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_buf: GuestPtrMut<u8>,
|
_buf: GuestPtr<u8>,
|
||||||
_buf_len: types::Size,
|
_buf_len: types::Size,
|
||||||
_cookie: types::Dircookie,
|
_cookie: types::Dircookie,
|
||||||
) -> Result<types::Size> {
|
) -> Result<types::Size> {
|
||||||
@@ -191,7 +191,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
unimplemented!("fd_write")
|
unimplemented!("fd_write")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_create_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> {
|
fn path_create_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||||
unimplemented!("path_create_directory")
|
unimplemented!("path_create_directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_flags: types::Lookupflags,
|
_flags: types::Lookupflags,
|
||||||
_path: &GuestString<'_>,
|
_path: &GuestPtr<'_, str>,
|
||||||
) -> Result<types::Filestat> {
|
) -> Result<types::Filestat> {
|
||||||
unimplemented!("path_filestat_get")
|
unimplemented!("path_filestat_get")
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_flags: types::Lookupflags,
|
_flags: types::Lookupflags,
|
||||||
_path: &GuestString<'_>,
|
_path: &GuestPtr<'_, str>,
|
||||||
_atim: types::Timestamp,
|
_atim: types::Timestamp,
|
||||||
_mtim: types::Timestamp,
|
_mtim: types::Timestamp,
|
||||||
_fst_flags: types::Fstflags,
|
_fst_flags: types::Fstflags,
|
||||||
@@ -220,9 +220,9 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
&self,
|
&self,
|
||||||
_old_fd: types::Fd,
|
_old_fd: types::Fd,
|
||||||
_old_flags: types::Lookupflags,
|
_old_flags: types::Lookupflags,
|
||||||
_old_path: &GuestString<'_>,
|
_old_path: &GuestPtr<'_, str>,
|
||||||
_new_fd: types::Fd,
|
_new_fd: types::Fd,
|
||||||
_new_path: &GuestString<'_>,
|
_new_path: &GuestPtr<'_, str>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("path_link")
|
unimplemented!("path_link")
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_dirflags: types::Lookupflags,
|
_dirflags: types::Lookupflags,
|
||||||
_path: &GuestString<'_>,
|
_path: &GuestPtr<'_, str>,
|
||||||
_oflags: types::Oflags,
|
_oflags: types::Oflags,
|
||||||
_fs_rights_base: types::Rights,
|
_fs_rights_base: types::Rights,
|
||||||
_fs_rights_inherting: types::Rights,
|
_fs_rights_inherting: types::Rights,
|
||||||
@@ -243,44 +243,44 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
fn path_readlink(
|
fn path_readlink(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_path: &GuestString<'_>,
|
_path: &GuestPtr<'_, str>,
|
||||||
_buf: GuestPtrMut<u8>,
|
_buf: GuestPtr<u8>,
|
||||||
_buf_len: types::Size,
|
_buf_len: types::Size,
|
||||||
) -> Result<types::Size> {
|
) -> Result<types::Size> {
|
||||||
unimplemented!("path_readlink")
|
unimplemented!("path_readlink")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> {
|
fn path_remove_directory(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||||
unimplemented!("path_remove_directory")
|
unimplemented!("path_remove_directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_rename(
|
fn path_rename(
|
||||||
&self,
|
&self,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_old_path: &GuestString<'_>,
|
_old_path: &GuestPtr<'_, str>,
|
||||||
_new_fd: types::Fd,
|
_new_fd: types::Fd,
|
||||||
_new_path: &GuestString<'_>,
|
_new_path: &GuestPtr<'_, str>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("path_rename")
|
unimplemented!("path_rename")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_symlink(
|
fn path_symlink(
|
||||||
&self,
|
&self,
|
||||||
_old_path: &GuestString<'_>,
|
_old_path: &GuestPtr<'_, str>,
|
||||||
_fd: types::Fd,
|
_fd: types::Fd,
|
||||||
_new_path: &GuestString<'_>,
|
_new_path: &GuestPtr<'_, str>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!("path_symlink")
|
unimplemented!("path_symlink")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestString<'_>) -> Result<()> {
|
fn path_unlink_file(&self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> {
|
||||||
unimplemented!("path_unlink_file")
|
unimplemented!("path_unlink_file")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_oneoff(
|
fn poll_oneoff(
|
||||||
&self,
|
&self,
|
||||||
_in_: GuestPtr<types::Subscription>,
|
_in_: GuestPtr<types::Subscription>,
|
||||||
_out: GuestPtrMut<types::Event>,
|
_out: GuestPtr<types::Event>,
|
||||||
_nsubscriptions: types::Size,
|
_nsubscriptions: types::Size,
|
||||||
) -> Result<types::Size> {
|
) -> Result<types::Size> {
|
||||||
unimplemented!("poll_oneoff")
|
unimplemented!("poll_oneoff")
|
||||||
@@ -298,7 +298,7 @@ impl crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
unimplemented!("sched_yield")
|
unimplemented!("sched_yield")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_get(&self, _buf: GuestPtrMut<u8>, _buf_len: types::Size) -> Result<()> {
|
fn random_get(&self, _buf: GuestPtr<u8>, _buf_len: types::Size) -> Result<()> {
|
||||||
unimplemented!("random_get")
|
unimplemented!("random_get")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user