multiple layers of pointers work!

This commit is contained in:
Pat Hickey
2020-01-24 19:57:18 -08:00
parent 020778b7da
commit a20ef36a49
8 changed files with 146 additions and 71 deletions

View File

@@ -0,0 +1,12 @@
use crate::Region;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GuestError {
#[error("Invalid enum value {0}")]
InvalidEnumValue(&'static str),
#[error("Out of bounds: {0:?}")]
PtrOutOfBounds(Region),
#[error("Borrowed: {0:?}")]
PtrBorrowed(Region),
}

View File

@@ -1,24 +1,17 @@
use crate::{GuestPtrMut, GuestPtrRead, MemoryError};
use thiserror::Error;
use crate::{GuestError, GuestPtrMut, GuestPtrRead};
pub trait GuestType: Sized {
fn size() -> u32;
fn name() -> &'static str;
}
#[derive(Debug, Error)]
pub enum GuestValueError {
#[error("Invalid enum {0}")]
InvalidEnum(&'static str),
}
pub trait GuestTypeCopy: GuestType + Copy {
fn read_val<P: GuestPtrRead<Self>>(src: &P) -> Result<Self, GuestValueError>;
fn read_val<'a, P: GuestPtrRead<'a, Self>>(src: &P) -> Result<Self, GuestError>;
fn write_val(val: Self, dest: &GuestPtrMut<Self>);
}
pub trait GuestTypeClone: GuestType + Clone {
fn read_ref<P: GuestPtrRead<Self>>(src: &P, dest: &mut Self) -> Result<(), GuestValueError>;
fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut Self) -> Result<(), GuestError>;
fn write_ref(val: &Self, dest: &GuestPtrMut<Self>);
}
@@ -26,7 +19,7 @@ impl<T> GuestTypeClone for T
where
T: GuestTypeCopy,
{
fn read_ref<P: GuestPtrRead<Self>>(src: &P, dest: &mut T) -> Result<(), GuestValueError> {
fn read_ref<'a, P: GuestPtrRead<'a, Self>>(src: &P, dest: &mut T) -> Result<(), GuestError> {
let val = GuestTypeCopy::read_val(src)?;
*dest = val;
Ok(())
@@ -49,7 +42,7 @@ macro_rules! builtin_copy {
}
impl GuestTypeCopy for $t {
fn read_val<P: GuestPtrRead<$t>>(src: &P) -> Result<$t, GuestValueError> {
fn read_val<'a, P: GuestPtrRead<'a, $t>>(src: &P) -> Result<$t, GuestError> {
Ok(unsafe {
::std::ptr::read_unaligned(src.ptr() as *const $t)
})
@@ -67,9 +60,8 @@ macro_rules! builtin_copy {
// These definitions correspond to all the witx BuiltinType variants that are Copy:
builtin_copy!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, usize, char);
pub trait GuestError {
pub trait GuestErrorType {
type Context;
fn success() -> Self;
fn from_memory_error(memory_error: MemoryError, ctx: &mut Self::Context) -> Self;
fn from_value_error(value_error: GuestValueError, ctx: &mut Self::Context) -> Self;
fn from_error(e: GuestError, ctx: &mut Self::Context) -> Self;
}

View File

@@ -1,8 +1,10 @@
mod borrow;
mod error;
mod guest_type;
mod memory;
mod region;
pub use guest_type::{GuestError, GuestType, GuestTypeClone, GuestTypeCopy, GuestValueError};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead, MemoryError};
pub use error::GuestError;
pub use guest_type::{GuestErrorType, GuestType, GuestTypeClone, GuestTypeCopy};
pub use memory::{GuestMemory, GuestPtr, GuestPtrMut, GuestPtrRead};
pub use region::Region;

View File

@@ -1,11 +1,9 @@
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use thiserror::Error;
use crate::borrow::{BorrowHandle, GuestBorrows};
use crate::guest_type::GuestType;
use crate::region::Region;
use crate::{GuestError, GuestType, Region};
pub struct GuestMemory<'a> {
ptr: *mut u8,
@@ -30,13 +28,13 @@ impl<'a> GuestMemory<'a> {
&& r.start < (self.len - r.len)
}
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>, GuestError> {
let region = Region {
start: at,
len: T::size(),
};
if !self.contains(region) {
Err(MemoryError::OutOfBounds(region))?;
Err(GuestError::PtrOutOfBounds(region))?;
}
let mut borrows = self.borrows.borrow_mut();
if let Some(handle) = borrows.borrow_immut(region) {
@@ -47,17 +45,17 @@ impl<'a> GuestMemory<'a> {
type_: PhantomData,
})
} else {
Err(MemoryError::Borrowed(region))
Err(GuestError::PtrBorrowed(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>, GuestError> {
let region = Region {
start: at,
len: T::size(),
};
if !self.contains(region) {
Err(MemoryError::OutOfBounds(region))?;
Err(GuestError::PtrOutOfBounds(region))?;
}
let mut borrows = self.borrows.borrow_mut();
if let Some(handle) = borrows.borrow_mut(region) {
@@ -68,13 +66,19 @@ impl<'a> GuestMemory<'a> {
type_: PhantomData,
})
} else {
Err(MemoryError::Borrowed(region))
Err(GuestError::PtrBorrowed(region))
}
}
}
pub trait GuestPtrRead<T> {
fn ptr(&self) -> *const u8;
/// These methods should not be used by the end user - just by implementations of the
/// GuestValueClone and GuestValueCopy traits!
pub trait GuestPtrRead<'a, T> {
fn mem(&self) -> &'a GuestMemory<'a>;
fn region(&self) -> &Region;
fn ptr(&self) -> *const u8 {
(self.mem().ptr as usize + self.region().start as usize) as *const u8
}
}
pub struct GuestPtr<'a, T> {
@@ -84,9 +88,31 @@ pub struct GuestPtr<'a, T> {
type_: PhantomData<T>,
}
impl<'a, T: GuestType> GuestPtrRead<T> for GuestPtr<'a, T> {
fn ptr(&self) -> *const u8 {
(self.mem.ptr as usize + self.region.start as usize) as *const u8
impl<'a, T> GuestPtrRead<'a, T> for GuestPtr<'a, T> {
fn mem(&self) -> &'a GuestMemory<'a> {
self.mem
}
fn region(&self) -> &Region {
&self.region
}
}
impl<'a, T> GuestType for GuestPtr<'a, T> {
fn size() -> u32 {
4
}
fn name() -> &'static str {
"GuestPtr<...>"
}
}
impl<'a, T: GuestType> GuestPtr<'a, T> {
pub fn read_ptr<P: GuestPtrRead<'a, Self>>(src: &P) -> Result<Self, GuestError> {
let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) };
src.mem().ptr(raw_ptr)
}
pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut<Self>) {
unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) }
}
}
@@ -104,9 +130,12 @@ pub struct GuestPtrMut<'a, 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> GuestPtrRead<'a, T> for GuestPtrMut<'a, T> {
fn mem(&self) -> &'a GuestMemory<'a> {
self.mem
}
fn region(&self) -> &Region {
&self.region
}
}
@@ -115,6 +144,7 @@ impl<'a, T> GuestPtrMut<'a, T> {
(self.mem.ptr as usize + self.region.start as usize) as *mut u8
}
}
impl<'a, T> Drop for GuestPtrMut<'a, T> {
fn drop(&mut self) {
let mut borrows = self.mem.borrows.borrow_mut();
@@ -122,10 +152,29 @@ impl<'a, T> Drop for GuestPtrMut<'a, T> {
}
}
#[derive(Debug, Error)]
pub enum MemoryError {
#[error("Out of bounds: {0:?}")]
OutOfBounds(Region),
#[error("Borrowed: {0:?}")]
Borrowed(Region),
impl<'a, T> GuestType for GuestPtrMut<'a, T> {
fn size() -> u32 {
4
}
fn name() -> &'static str {
"GuestPtrMut<...>"
}
}
impl<'a, T: GuestType> GuestPtrMut<'a, T> {
pub fn read_ptr<P: GuestPtrRead<'a, Self>>(src: &P) -> Result<Self, GuestError> {
let raw_ptr = unsafe { ::std::ptr::read_unaligned(src.ptr() as *const u32) };
src.mem().ptr_mut(raw_ptr)
}
pub fn write_ptr(ptr: &Self, dest: &GuestPtrMut<Self>) {
unsafe { ::std::ptr::write_unaligned(dest.ptr_mut() as *mut u32, ptr.region.start) }
}
pub fn as_immut(self) -> GuestPtr<'a, T> {
let mem = self.mem;
let start = self.region.start;
drop(self);
mem.ptr(start)
.expect("can borrow just-dropped mutable region as immut")
}
}