we can handle one layer of pointers!

This commit is contained in:
Pat Hickey
2020-01-24 18:08:37 -08:00
parent e789033651
commit 020778b7da
9 changed files with 155 additions and 86 deletions

View File

@@ -111,21 +111,25 @@ fn marshal_arg(
let interface_typename = names.type_ref(&tref); let interface_typename = names.type_ref(&tref);
let name = names.func_param(&param.name); let name = names.func_param(&param.name);
let value_error_handling = if let Some(tref) = error_type { let error_handling = |method| -> TokenStream {
let abi_ret = match tref.type_().passed_by() { if let Some(tref) = error_type {
witx::TypePassedBy::Value(atom) => names.atom_type(atom), let abi_ret = match tref.type_().passed_by() {
_ => unreachable!("err should always be passed by value"), witx::TypePassedBy::Value(atom) => names.atom_type(atom),
}; _ => unreachable!("err should always be passed by value"),
let err_typename = names.type_ref(&tref); };
quote! { let err_typename = names.type_ref(&tref);
let err: #err_typename = ::memory::GuestError::from_value_error(e, ctx); quote! {
return #abi_ret::from(err); let err: #err_typename = ::memory::GuestError::#method(e, ctx);
} return #abi_ret::from(err);
} else { }
quote! { } else {
panic!("memory error: {:?}", e) quote! {
panic!("error: {:?}", e)
}
} }
}; };
let value_error_handling = error_handling(quote!(from_value_error));
let memory_error_handling = error_handling(quote!(from_memory_error));
let try_into_conversion = quote! { let try_into_conversion = quote! {
use ::std::convert::TryInto; use ::std::convert::TryInto;
@@ -162,6 +166,28 @@ fn marshal_arg(
}, },
witx::BuiltinType::String => unimplemented!("string types unimplemented"), witx::BuiltinType::String => unimplemented!("string types unimplemented"),
}, },
_ => unimplemented!("only enums and builtins so far"), witx::Type::Pointer(pointee) => {
let pointee_type = names.type_ref(pointee);
quote! {
let #name = match memory.ptr_mut::<#pointee_type>(#name as u32) {
Ok(p) => p,
Err(e) => {
#memory_error_handling
}
};
}
}
witx::Type::ConstPointer(pointee) => {
let pointee_type = names.type_ref(pointee);
quote! {
let #name = match memory.ptr::<#pointee_type>(#name as u32) {
Ok(p) => p,
Err(e) => {
#memory_error_handling
}
};
}
}
_ => unimplemented!("argument type marshalling"),
} }
} }

View File

@@ -50,6 +50,14 @@ impl Names {
} }
TypeRef::Value(ty) => match &**ty { TypeRef::Value(ty) => match &**ty {
witx::Type::Builtin(builtin) => self.builtin_type(*builtin), witx::Type::Builtin(builtin) => self.builtin_type(*builtin),
witx::Type::Pointer(pointee) => {
let pointee_type = self.type_ref(&pointee);
quote!(::memory::GuestPtrMut<#pointee_type>)
}
witx::Type::ConstPointer(pointee) => {
let pointee_type = self.type_ref(&pointee);
quote!(::memory::GuestPtr<#pointee_type>)
}
_ => unimplemented!("anonymous type ref"), _ => unimplemented!("anonymous type ref"),
}, },
} }

View File

@@ -95,12 +95,12 @@ fn define_enum(names: &Names, name: &witx::Id, e: &witx::EnumDatatype) -> TokenS
} }
impl ::memory::GuestTypeCopy for #ident { impl ::memory::GuestTypeCopy for #ident {
fn read_val(src: ::memory::GuestPtr<#ident>) -> Result<#ident, ::memory::GuestValueError> { fn read_val<P: ::memory::GuestPtrRead<#ident>>(src: &P) -> Result<#ident, ::memory::GuestValueError> {
use ::std::convert::TryInto; use ::std::convert::TryInto;
let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) }; let val = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const #repr) };
val.try_into() val.try_into()
} }
fn write_val(val: #ident, dest: ::memory::GuestPtrMut<#ident>) { fn write_val(val: #ident, dest: &::memory::GuestPtrMut<#ident>) {
let val: #repr = val.into(); let val: #repr = val.into();
unsafe { unsafe {
::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val) ::std::ptr::write_unaligned(dest.ptr_mut() as *mut #repr, val)

View File

@@ -1,59 +1,66 @@
use std::collections::HashMap;
use crate::region::Region; use crate::region::Region;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct BorrowHandle(usize);
pub struct GuestBorrows { pub struct GuestBorrows {
immutable: Vec<Region>, immutable: HashMap<BorrowHandle, Region>,
mutable: Vec<Region>, mutable: HashMap<BorrowHandle, Region>,
next_handle: BorrowHandle,
} }
impl GuestBorrows { impl GuestBorrows {
pub fn new() -> Self { pub fn new() -> Self {
GuestBorrows { GuestBorrows {
immutable: Vec::new(), immutable: HashMap::new(),
mutable: Vec::new(), mutable: HashMap::new(),
next_handle: BorrowHandle(0),
} }
} }
fn is_borrowed_immut(&self, r: Region) -> bool { fn is_borrowed_immut(&self, r: Region) -> bool {
!self.immutable.iter().all(|b| !b.overlaps(r)) !self.immutable.values().all(|b| !b.overlaps(r))
} }
fn is_borrowed_mut(&self, r: Region) -> bool { fn is_borrowed_mut(&self, r: Region) -> bool {
!self.mutable.iter().all(|b| !b.overlaps(r)) !self.mutable.values().all(|b| !b.overlaps(r))
} }
pub fn borrow_immut(&mut self, r: Region) -> bool { 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) { if self.is_borrowed_mut(r) {
return false; return None;
} }
self.immutable.push(r); let h = self.new_handle();
true self.immutable.insert(h, r);
Some(h)
} }
pub fn unborrow_immut(&mut self, r: Region) { pub fn unborrow_immut(&mut self, h: BorrowHandle) {
let (ix, _) = self self.immutable
.immutable .remove(&h)
.iter() .expect("handle exists in immutable borrows");
.enumerate()
.find(|(_, reg)| r == **reg)
.expect("region exists in borrows");
self.immutable.remove(ix);
} }
pub fn borrow_mut(&mut self, r: Region) -> bool { pub fn borrow_mut(&mut self, r: Region) -> Option<BorrowHandle> {
if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) {
return false; return None;
} }
self.mutable.push(r); let h = self.new_handle();
true self.mutable.insert(h, r);
Some(h)
} }
pub fn unborrow_mut(&mut self, r: Region) { pub fn unborrow_mut(&mut self, h: BorrowHandle) {
let (ix, _) = self self.mutable
.mutable .remove(&h)
.iter() .expect("handle exists in mutable borrows");
.enumerate()
.find(|(_, reg)| r == **reg)
.expect("region exists in borrows");
self.mutable.remove(ix);
} }
} }

View File

@@ -1,4 +1,4 @@
use crate::{GuestPtr, GuestPtrMut, MemoryError}; use crate::{GuestPtrMut, GuestPtrRead, MemoryError};
use thiserror::Error; use thiserror::Error;
pub trait GuestType: Sized { pub trait GuestType: Sized {
@@ -13,25 +13,25 @@ pub enum GuestValueError {
} }
pub trait GuestTypeCopy: GuestType + Copy { pub trait GuestTypeCopy: GuestType + Copy {
fn read_val(src: GuestPtr<Self>) -> Result<Self, GuestValueError>; fn read_val<P: GuestPtrRead<Self>>(src: &P) -> Result<Self, GuestValueError>;
fn write_val(val: Self, dest: GuestPtrMut<Self>); fn write_val(val: Self, dest: &GuestPtrMut<Self>);
} }
pub trait GuestTypeClone: GuestType + Clone { pub trait GuestTypeClone: GuestType + Clone {
fn read_ref(src: GuestPtr<Self>, dest: &mut Self) -> Result<(), GuestValueError>; fn read_ref<P: GuestPtrRead<Self>>(src: &P, dest: &mut Self) -> Result<(), GuestValueError>;
fn write_ref(val: &Self, dest: GuestPtrMut<Self>); fn write_ref(val: &Self, dest: &GuestPtrMut<Self>);
} }
impl<T> GuestTypeClone for T impl<T> GuestTypeClone for T
where where
T: GuestTypeCopy, T: GuestTypeCopy,
{ {
fn read_ref(src: GuestPtr<T>, dest: &mut T) -> Result<(), GuestValueError> { fn read_ref<P: GuestPtrRead<Self>>(src: &P, dest: &mut T) -> Result<(), GuestValueError> {
let val = GuestTypeCopy::read_val(src)?; let val = GuestTypeCopy::read_val(src)?;
*dest = val; *dest = val;
Ok(()) Ok(())
} }
fn write_ref(val: &T, dest: GuestPtrMut<T>) { fn write_ref(val: &T, dest: &GuestPtrMut<T>) {
GuestTypeCopy::write_val(*val, dest) GuestTypeCopy::write_val(*val, dest)
} }
} }
@@ -49,12 +49,12 @@ macro_rules! builtin_copy {
} }
impl GuestTypeCopy for $t { impl GuestTypeCopy for $t {
fn read_val(src: GuestPtr<$t>) -> Result<$t, GuestValueError> { fn read_val<P: GuestPtrRead<$t>>(src: &P) -> Result<$t, GuestValueError> {
Ok(unsafe { Ok(unsafe {
::std::ptr::read_unaligned(src.ptr() as *const $t) ::std::ptr::read_unaligned(src.ptr() as *const $t)
}) })
} }
fn write_val(val: $t, dest: GuestPtrMut<$t>) { fn write_val(val: $t, dest: &GuestPtrMut<$t>) {
unsafe { unsafe {
::std::ptr::write_unaligned(dest.ptr_mut() as *mut $t, val) ::std::ptr::write_unaligned(dest.ptr_mut() as *mut $t, val)
} }

View File

@@ -4,5 +4,5 @@ mod memory;
mod region; mod region;
pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError}; pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, MemoryError}; pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead, MemoryError};
pub use region::Region; pub use region::Region;

View File

@@ -3,7 +3,7 @@ use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use thiserror::Error; use thiserror::Error;
use crate::borrow::GuestBorrows; use crate::borrow::{BorrowHandle, GuestBorrows};
use crate::guest_type::GuestType; use crate::guest_type::GuestType;
use crate::region::Region; use crate::region::Region;
@@ -31,80 +31,85 @@ impl<'a> GuestMemory<'a> {
} }
pub fn ptr<T: GuestType>(&'a self, at: u32) -> Result<GuestPtr<'a, T>, MemoryError> { pub fn ptr<T: GuestType>(&'a self, at: u32) -> Result<GuestPtr<'a, T>, MemoryError> {
let r = Region { let region = Region {
start: at, start: at,
len: T::size(), len: T::size(),
}; };
let mut borrows = self.borrows.borrow_mut(); if !self.contains(region) {
if !self.contains(r) { Err(MemoryError::OutOfBounds(region))?;
Err(MemoryError::OutOfBounds(r))?;
} }
if borrows.borrow_immut(r) { let mut borrows = self.borrows.borrow_mut();
if let Some(handle) = borrows.borrow_immut(region) {
Ok(GuestPtr { Ok(GuestPtr {
mem: &self, mem: &self,
region: r, region,
handle,
type_: PhantomData, type_: PhantomData,
}) })
} else { } else {
Err(MemoryError::Borrowed(r)) Err(MemoryError::Borrowed(region))
} }
} }
pub fn ptr_mut<T: GuestType>(&'a self, at: u32) -> Result<GuestPtrMut<'a, T>, MemoryError> { pub fn ptr_mut<T: GuestType>(&'a self, at: u32) -> Result<GuestPtrMut<'a, T>, MemoryError> {
let r = Region { let region = Region {
start: at, start: at,
len: T::size(), len: T::size(),
}; };
let mut borrows = self.borrows.borrow_mut(); if !self.contains(region) {
if !self.contains(r) { Err(MemoryError::OutOfBounds(region))?;
Err(MemoryError::OutOfBounds(r))?;
} }
if borrows.borrow_mut(r) { let mut borrows = self.borrows.borrow_mut();
if let Some(handle) = borrows.borrow_mut(region) {
Ok(GuestPtrMut { Ok(GuestPtrMut {
mem: &self, mem: &self,
region: r, region,
handle,
type_: PhantomData, type_: PhantomData,
}) })
} else { } else {
Err(MemoryError::Borrowed(r)) Err(MemoryError::Borrowed(region))
} }
} }
} }
pub trait GuestPtrRead<T> {
fn ptr(&self) -> *const u8;
}
pub struct GuestPtr<'a, T> { pub struct GuestPtr<'a, T> {
mem: &'a GuestMemory<'a>, mem: &'a GuestMemory<'a>,
region: Region, region: Region,
handle: BorrowHandle,
type_: PhantomData<T>, type_: PhantomData<T>,
} }
impl<'a, T: GuestType> GuestPtr<'a, T> { impl<'a, T: GuestType> GuestPtrRead<T> for GuestPtr<'a, T> {
pub fn ptr(&self) -> *const u8 { fn ptr(&self) -> *const u8 {
(self.mem.ptr as usize + self.region.start as usize) as *const u8 (self.mem.ptr as usize + self.region.start as usize) as *const u8
} }
pub unsafe fn downcast<Q: GuestType>(self) -> GuestPtr<'a, Q> {
debug_assert!(T::size() == Q::size(), "downcast to type of same size");
GuestPtr {
mem: self.mem,
region: self.region,
type_: PhantomData,
}
}
} }
impl<'a, T> Drop for GuestPtr<'a, T> { impl<'a, T> Drop for GuestPtr<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
let mut borrows = self.mem.borrows.borrow_mut(); let mut borrows = self.mem.borrows.borrow_mut();
borrows.unborrow_immut(self.region); borrows.unborrow_immut(self.handle);
} }
} }
pub struct GuestPtrMut<'a, T> { pub struct GuestPtrMut<'a, T> {
mem: &'a GuestMemory<'a>, mem: &'a GuestMemory<'a>,
region: Region, region: Region,
handle: BorrowHandle,
type_: PhantomData<T>, type_: PhantomData<T>,
} }
impl<'a, T: GuestType> GuestPtrRead<T> for GuestPtrMut<'a, T> {
fn ptr(&self) -> *const u8 {
(self.mem.ptr as usize + self.region.start as usize) as *const u8
}
}
impl<'a, T> GuestPtrMut<'a, T> { impl<'a, T> GuestPtrMut<'a, T> {
pub fn ptr_mut(&self) -> *mut u8 { pub fn ptr_mut(&self) -> *mut u8 {
(self.mem.ptr as usize + self.region.start as usize) as *mut u8 (self.mem.ptr as usize + self.region.start as usize) as *mut u8
@@ -113,7 +118,7 @@ impl<'a, T> GuestPtrMut<'a, T> {
impl<'a, T> Drop for GuestPtrMut<'a, T> { impl<'a, T> Drop for GuestPtrMut<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
let mut borrows = self.mem.borrows.borrow_mut(); let mut borrows = self.mem.borrows.borrow_mut();
borrows.unborrow_mut(self.region); borrows.unborrow_mut(self.handle);
} }
} }

View File

@@ -12,8 +12,29 @@ pub mod test {
println!("BAR: {} {}", an_int, an_float); println!("BAR: {} {}", an_int, an_float);
Ok(()) Ok(())
} }
fn baz(&mut self, excuse: types::Excuse) -> Result<(), types::Errno> { fn baz(
println!("BAZ: {:?}", excuse); &mut self,
excuse: types::Excuse,
a_better_excuse_by_reference: ::memory::GuestPtrMut<types::Excuse>,
a_lamer_excuse_by_reference: ::memory::GuestPtr<types::Excuse>,
) -> Result<(), types::Errno> {
use memory::GuestTypeCopy;
let a_better_excuse =
types::Excuse::read_val(&a_better_excuse_by_reference).map_err(|val_err| {
eprintln!("a_better_excuse_by_reference value error: {:?}", val_err);
types::Errno::InvalidArg
})?;
let a_lamer_excuse =
types::Excuse::read_val(&a_lamer_excuse_by_reference).map_err(|val_err| {
eprintln!("a_lamer_excuse_by_reference value error: {:?}", val_err);
types::Errno::InvalidArg
})?;
types::Excuse::write_val(a_lamer_excuse, &a_better_excuse_by_reference);
println!(
"BAZ: {:?} {:?} {:?}",
excuse, a_better_excuse, a_lamer_excuse
);
Ok(()) Ok(())
} }
} }

View File

@@ -21,5 +21,7 @@
(result $error $errno)) (result $error $errno))
(@interface func (export "baz") (@interface func (export "baz")
(param $an_excuse $excuse) (param $an_excuse $excuse)
(param $an_excuse_by_reference (@witx pointer $excuse))
(param $a_lamer_excuse (@witx const_pointer $excuse))
(result $error $errno)) (result $error $errno))
) )