use anyhow::{bail, Result};
use std::cell::UnsafeCell;
use std::fmt;
use std::mem;
use std::slice;
use std::str;
use std::sync::Arc;
pub use wiggle_macro::{async_trait, from_witx};
pub use anyhow;
pub use wiggle_macro::wasmtime_integration;
pub use bitflags;
#[cfg(feature = "wiggle_metadata")]
pub use witx;
pub mod borrow;
mod error;
mod guest_type;
mod region;
pub extern crate tracing;
pub use error::GuestError;
pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};
pub use region::Region;
pub mod async_trait_crate {
pub use async_trait::*;
}
pub mod wasmtime;
pub mod wasmtime_crate {
pub use wasmtime::*;
}
pub unsafe trait GuestMemory: Send + Sync {
fn base(&self) -> &[UnsafeCell<u8>];
fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T>
where
Self: Sized,
T: ?Sized + Pointee,
{
GuestPtr::new(self, offset)
}
fn has_outstanding_borrows(&self) -> bool;
fn is_mut_borrowed(&self, r: Region) -> bool;
fn is_shared_borrowed(&self, r: Region) -> bool;
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError>;
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError>;
fn mut_unborrow(&self, h: BorrowHandle);
fn shared_unborrow(&self, h: BorrowHandle);
fn is_shared_memory(&self) -> bool {
false
}
}
fn validate_size_align<'a, T: GuestTypeTransparent<'a>>(
mem: &'a dyn GuestMemory,
offset: u32,
len: u32,
) -> Result<(&[UnsafeCell<T>], Region), GuestError> {
let base = mem.base();
let byte_len = len
.checked_mul(T::guest_size())
.ok_or(GuestError::PtrOverflow)?;
let region = Region {
start: offset,
len: byte_len,
};
let offset = usize::try_from(offset)?;
let byte_len = usize::try_from(byte_len)?;
let bytes = base
.get(offset..)
.and_then(|s| s.get(..byte_len))
.ok_or(GuestError::PtrOutOfBounds(region))?;
assert!(mem::align_of::<T>() <= T::guest_align());
let (start, mid, end) = unsafe { bytes.align_to() };
if start.len() > 0 || end.len() > 0 {
return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32));
}
Ok((mid, region))
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct BorrowHandle(pub usize);
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T {
fn base(&self) -> &[UnsafeCell<u8>] {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_mut_borrowed(&self, r: Region) -> bool {
T::is_mut_borrowed(self, r)
}
fn is_shared_borrowed(&self, r: Region) -> bool {
T::is_shared_borrowed(self, r)
}
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::mut_borrow(self, r)
}
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::shared_borrow(self, r)
}
fn mut_unborrow(&self, h: BorrowHandle) {
T::mut_unborrow(self, h)
}
fn shared_unborrow(&self, h: BorrowHandle) {
T::shared_unborrow(self, h)
}
}
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T {
fn base(&self) -> &[UnsafeCell<u8>] {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_mut_borrowed(&self, r: Region) -> bool {
T::is_mut_borrowed(self, r)
}
fn is_shared_borrowed(&self, r: Region) -> bool {
T::is_shared_borrowed(self, r)
}
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::mut_borrow(self, r)
}
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::shared_borrow(self, r)
}
fn mut_unborrow(&self, h: BorrowHandle) {
T::mut_unborrow(self, h)
}
fn shared_unborrow(&self, h: BorrowHandle) {
T::shared_unborrow(self, h)
}
}
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Box<T> {
fn base(&self) -> &[UnsafeCell<u8>] {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_mut_borrowed(&self, r: Region) -> bool {
T::is_mut_borrowed(self, r)
}
fn is_shared_borrowed(&self, r: Region) -> bool {
T::is_shared_borrowed(self, r)
}
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::mut_borrow(self, r)
}
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::shared_borrow(self, r)
}
fn mut_unborrow(&self, h: BorrowHandle) {
T::mut_unborrow(self, h)
}
fn shared_unborrow(&self, h: BorrowHandle) {
T::shared_unborrow(self, h)
}
}
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Arc<T> {
fn base(&self) -> &[UnsafeCell<u8>] {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_mut_borrowed(&self, r: Region) -> bool {
T::is_mut_borrowed(self, r)
}
fn is_shared_borrowed(&self, r: Region) -> bool {
T::is_shared_borrowed(self, r)
}
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::mut_borrow(self, r)
}
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::shared_borrow(self, r)
}
fn mut_unborrow(&self, h: BorrowHandle) {
T::mut_unborrow(self, h)
}
fn shared_unborrow(&self, h: BorrowHandle) {
T::shared_unborrow(self, h)
}
}
pub struct GuestPtr<'a, T: ?Sized + Pointee> {
mem: &'a (dyn GuestMemory + 'a),
pointer: T::Pointer,
}
impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> {
pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'a, T> {
GuestPtr { mem, pointer }
}
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::PtrOverflow),
};
Ok(GuestPtr::new(self.mem, offset))
}
pub fn as_array(&self, elems: u32) -> GuestPtr<'a, [T]>
where
T: GuestType<'a> + Pointee<Pointer = u32>,
{
GuestPtr::new(self.mem, (self.pointer, elems))
}
pub fn is_shared_memory(&self) -> bool {
self.mem.is_shared_memory()
}
}
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 = self.as_ptr();
(0..self.len()).map(move |i| base.add(i))
}
pub fn as_cow(&self) -> Result<GuestCow<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a> + Copy + 'a,
{
match self.as_unsafe_slice_mut()?.shared_borrow() {
UnsafeBorrowResult::Ok(slice) => Ok(GuestCow::Borrowed(slice)),
UnsafeBorrowResult::Shared(_) => Ok(GuestCow::Copied(self.to_vec()?)),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
pub fn as_slice(&self) -> Result<Option<GuestSlice<'a, T>>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
match self.as_unsafe_slice_mut()?.shared_borrow() {
UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)),
UnsafeBorrowResult::Shared(_) => Ok(None),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
pub fn as_slice_mut(&self) -> Result<Option<GuestSliceMut<'a, T>>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
self.as_unsafe_slice_mut()?.as_slice_mut()
}
pub fn as_unsafe_slice_mut(&self) -> Result<UnsafeGuestSlice<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
let (ptr, region) = validate_size_align(self.mem, self.pointer.0, self.pointer.1)?;
Ok(UnsafeGuestSlice {
ptr,
region,
mem: self.mem,
})
}
pub fn to_vec(&self) -> Result<Vec<T>, GuestError>
where
T: GuestTypeTransparent<'a> + Copy + 'a,
{
let guest_slice = self.as_unsafe_slice_mut()?;
let len = guest_slice.ptr.len();
let mut vec = Vec::with_capacity(len);
unsafe {
std::ptr::copy(guest_slice.ptr.as_ptr().cast::<T>(), vec.as_mut_ptr(), len);
vec.set_len(len);
}
Ok(vec)
}
pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError>
where
T: GuestTypeTransparent<'a> + Copy + 'a,
{
self.as_unsafe_slice_mut()?.copy_from_slice(slice)
}
pub fn as_ptr(&self) -> GuestPtr<'a, T> {
GuestPtr::new(self.mem, self.offset_base())
}
pub fn get(&self, index: u32) -> Option<GuestPtr<'a, T>>
where
T: GuestType<'a>,
{
if index < self.len() {
Some(
self.as_ptr()
.add(index)
.expect("just performed bounds check"),
)
} else {
None
}
}
pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<'a, [T]>>
where
T: GuestType<'a>,
{
if r.end < r.start {
return None;
}
let range_length = r.end - r.start;
if r.start <= self.len() && r.end <= self.len() {
Some(
self.as_ptr()
.add(r.start)
.expect("just performed bounds check")
.as_array(range_length),
)
} else {
None
}
}
}
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_str(&self) -> Result<Option<GuestStr<'a>>, GuestError> {
match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() {
UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)),
UnsafeBorrowResult::Shared(_) => Ok(None),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
pub fn as_str_mut(&self) -> Result<Option<GuestStrMut<'a>>, GuestError> {
match self.as_bytes().as_unsafe_slice_mut()?.mut_borrow() {
UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)),
UnsafeBorrowResult::Shared(_) => Ok(None),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
pub fn as_cow(&self) -> Result<GuestStrCow<'a>, GuestError> {
match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() {
UnsafeBorrowResult::Ok(s) => Ok(GuestStrCow::Borrowed(s.try_into()?)),
UnsafeBorrowResult::Shared(_) => {
let copied = self.as_bytes().to_vec()?;
let utf8_string = String::from_utf8(copied).map_err(|e| e.utf8_error())?;
Ok(GuestStrCow::Copied(utf8_string))
}
UnsafeBorrowResult::Err(e) => Err(e),
}
}
}
impl<'a> GuestPtr<'a, [u8]> {
pub fn as_str_ptr(&self) -> GuestPtr<'a, str> {
GuestPtr::new(self.mem, self.pointer)
}
}
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)
}
}
pub struct GuestSlice<'a, T> {
ptr: &'a [UnsafeCell<T>],
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
unsafe impl<T: Send> Send for GuestSlice<'_, T> {}
unsafe impl<T: Sync> Sync for GuestSlice<'_, T> {}
impl<'a, T> std::ops::Deref for GuestSlice<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr.as_ptr().cast(), self.ptr.len()) }
}
}
impl<'a, T> Drop for GuestSlice<'a, T> {
fn drop(&mut self) {
self.mem.shared_unborrow(self.borrow)
}
}
pub struct GuestSliceMut<'a, T> {
ptr: &'a [UnsafeCell<T>],
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
unsafe impl<T: Send> Send for GuestSliceMut<'_, T> {}
unsafe impl<T: Sync> Sync for GuestSliceMut<'_, T> {}
impl<'a, T> std::ops::Deref for GuestSliceMut<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr.as_ptr().cast(), self.ptr.len()) }
}
}
impl<'a, T> std::ops::DerefMut for GuestSliceMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr() as *mut T, self.ptr.len()) }
}
}
impl<'a, T> Drop for GuestSliceMut<'a, T> {
fn drop(&mut self) {
self.mem.mut_unborrow(self.borrow)
}
}
pub enum GuestCow<'a, T> {
Borrowed(GuestSlice<'a, T>),
Copied(Vec<T>),
}
impl<'a, T> std::ops::Deref for GuestCow<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
match self {
GuestCow::Borrowed(s) => s,
GuestCow::Copied(s) => s,
}
}
}
pub struct UnsafeGuestSlice<'a, T> {
ptr: &'a [UnsafeCell<T>],
region: Region,
mem: &'a dyn GuestMemory,
}
unsafe impl<T: Sync> Sync for UnsafeGuestSlice<'_, T> {}
unsafe impl<T: Send> Send for UnsafeGuestSlice<'_, T> {}
impl<'a, T> UnsafeGuestSlice<'a, T> {
pub fn copy_from_slice(self, slice: &[T]) -> Result<(), GuestError>
where
T: GuestTypeTransparent<'a> + Copy + 'a,
{
if self.ptr.len() != slice.len() {
return Err(GuestError::SliceLengthsDiffer);
}
if slice.len() == 0 {
return Ok(());
}
match self.mut_borrow() {
UnsafeBorrowResult::Ok(mut dst) => dst.copy_from_slice(slice),
UnsafeBorrowResult::Shared(guest_slice) => {
unsafe {
std::ptr::copy(
slice.as_ptr(),
guest_slice.ptr[0].get(),
guest_slice.ptr.len(),
)
};
}
UnsafeBorrowResult::Err(e) => return Err(e),
}
Ok(())
}
pub fn len(&self) -> usize {
self.ptr.len()
}
pub fn is_shared_memory(&self) -> bool {
self.mem.is_shared_memory()
}
pub fn as_slice_mut(self) -> Result<Option<GuestSliceMut<'a, T>>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
match self.mut_borrow() {
UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)),
UnsafeBorrowResult::Shared(_) => Ok(None),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
fn shared_borrow(self) -> UnsafeBorrowResult<GuestSlice<'a, T>, Self> {
if self.mem.is_shared_memory() {
UnsafeBorrowResult::Shared(self)
} else {
match self.mem.shared_borrow(self.region) {
Ok(borrow) => UnsafeBorrowResult::Ok(GuestSlice {
ptr: self.ptr,
mem: self.mem,
borrow,
}),
Err(e) => UnsafeBorrowResult::Err(e),
}
}
}
fn mut_borrow(self) -> UnsafeBorrowResult<GuestSliceMut<'a, T>, Self> {
if self.mem.is_shared_memory() {
UnsafeBorrowResult::Shared(self)
} else {
match self.mem.mut_borrow(self.region) {
Ok(borrow) => UnsafeBorrowResult::Ok(GuestSliceMut {
ptr: self.ptr,
mem: self.mem,
borrow,
}),
Err(e) => UnsafeBorrowResult::Err(e),
}
}
}
}
enum UnsafeBorrowResult<T, S> {
Ok(T),
Shared(S),
Err(GuestError),
}
impl<T, S> From<GuestError> for UnsafeBorrowResult<T, S> {
fn from(e: GuestError) -> Self {
UnsafeBorrowResult::Err(e)
}
}
pub struct GuestStr<'a>(GuestSlice<'a, u8>);
impl<'a> std::convert::TryFrom<GuestSlice<'a, u8>> for GuestStr<'a> {
type Error = GuestError;
fn try_from(slice: GuestSlice<'a, u8>) -> Result<Self, Self::Error> {
match str::from_utf8(&slice) {
Ok(_) => Ok(Self(slice)),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
}
impl<'a> std::ops::Deref for GuestStr<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
unsafe { str::from_utf8_unchecked(&self.0) }
}
}
pub struct GuestStrMut<'a>(GuestSliceMut<'a, u8>);
impl<'a> std::convert::TryFrom<GuestSliceMut<'a, u8>> for GuestStrMut<'a> {
type Error = GuestError;
fn try_from(slice: GuestSliceMut<'a, u8>) -> Result<Self, Self::Error> {
match str::from_utf8(&slice) {
Ok(_) => Ok(Self(slice)),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
}
impl<'a> std::ops::Deref for GuestStrMut<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
unsafe { str::from_utf8_unchecked(&self.0) }
}
}
impl<'a> std::ops::DerefMut for GuestStrMut<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
}
}
pub enum GuestStrCow<'a> {
Borrowed(GuestStr<'a>),
Copied(String),
}
impl<'a> std::ops::Deref for GuestStrCow<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
GuestStrCow::Borrowed(s) => s,
GuestStrCow::Copied(s) => s,
}
}
}
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)
}
}
pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output> {
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
let mut f = Pin::from(Box::new(future));
let waker = dummy_waker();
let mut cx = Context::from_waker(&waker);
match f.as_mut().poll(&mut cx) {
Poll::Ready(val) => return Ok(val),
Poll::Pending =>
bail!("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"),
}
fn dummy_waker() -> Waker {
return unsafe { Waker::from_raw(clone(5 as *const _)) };
unsafe fn clone(ptr: *const ()) -> RawWaker {
assert_eq!(ptr as usize, 5);
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
RawWaker::new(ptr, &VTABLE)
}
unsafe fn wake(ptr: *const ()) {
assert_eq!(ptr as usize, 5);
}
unsafe fn wake_by_ref(ptr: *const ()) {
assert_eq!(ptr as usize, 5);
}
unsafe fn drop(ptr: *const ()) {
assert_eq!(ptr as usize, 5);
}
}
}