use std::fmt;
use std::slice;
use std::str;
use std::sync::Arc;
pub use wiggle_macro::{async_trait, from_witx};
#[cfg(feature = "wasmtime")]
pub use anyhow;
#[cfg(feature = "wasmtime")]
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::*;
}
#[cfg(feature = "wasmtime")]
pub mod wasmtime;
#[cfg(feature = "wasmtime")]
pub mod wasmtime_crate {
pub use wasmtime::*;
}
pub unsafe trait GuestMemory: Send + Sync {
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 };
let start = match (base_ptr as usize).checked_add(offset as usize) {
Some(ptr) => ptr,
None => return Err(GuestError::PtrOverflow),
};
let end = match start.checked_add(len as usize) {
Some(ptr) => ptr,
None => return Err(GuestError::PtrOverflow),
};
if end > (base_ptr as usize) + (base_len as usize) {
return Err(GuestError::PtrOutOfBounds(region));
}
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)
}
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);
}
#[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) -> (*mut u8, u32) {
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) -> (*mut u8, u32) {
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) -> (*mut u8, u32) {
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) -> (*mut u8, u32) {
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))
}
}
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_slice(&self) -> Result<GuestSlice<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
let len = match self.pointer.1.checked_mul(T::guest_size()) {
Some(l) => l,
None => return Err(GuestError::PtrOverflow),
};
let ptr =
self.mem
.validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T;
let borrow = self.mem.shared_borrow(Region {
start: self.pointer.0,
len,
})?;
for offs in 0..self.pointer.1 {
T::validate(unsafe { ptr.add(offs as usize) })?;
}
let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) };
Ok(GuestSlice {
ptr,
mem: self.mem,
borrow,
})
}
pub fn as_slice_mut(&self) -> Result<GuestSliceMut<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
let len = match self.pointer.1.checked_mul(T::guest_size()) {
Some(l) => l,
None => return Err(GuestError::PtrOverflow),
};
let ptr =
self.mem
.validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T;
let borrow = self.mem.mut_borrow(Region {
start: self.pointer.0,
len,
})?;
for offs in 0..self.pointer.1 {
T::validate(unsafe { ptr.add(offs as usize) })?;
}
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
Ok(GuestSliceMut {
ptr,
mem: self.mem,
borrow,
})
}
pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError>
where
T: GuestTypeTransparent<'a> + Copy + 'a,
{
let mut self_slice = self.as_slice_mut()?;
if self_slice.len() != slice.len() {
return Err(GuestError::SliceLengthsDiffer);
}
self_slice.copy_from_slice(slice);
Ok(())
}
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_byte_ptr(&self) -> GuestPtr<'a, [u8]> {
GuestPtr::new(self.mem, self.pointer)
}
pub fn as_str(&self) -> Result<GuestStr<'a>, GuestError> {
let ptr = self
.mem
.validate_size_align(self.pointer.0, 1, self.pointer.1)?;
let borrow = self.mem.shared_borrow(Region {
start: self.pointer.0,
len: self.pointer.1,
})?;
let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) };
match str::from_utf8(ptr) {
Ok(ptr) => Ok(GuestStr {
ptr,
mem: self.mem,
borrow,
}),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
pub fn as_str_mut(&self) -> Result<GuestStrMut<'a>, GuestError> {
let ptr = self
.mem
.validate_size_align(self.pointer.0, 1, self.pointer.1)?;
let borrow = self.mem.mut_borrow(Region {
start: self.pointer.0,
len: self.pointer.1,
})?;
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
match str::from_utf8_mut(ptr) {
Ok(ptr) => Ok(GuestStrMut {
ptr,
mem: self.mem,
borrow,
}),
Err(e) => Err(GuestError::InvalidUtf8(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 [T],
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a, T> std::ops::Deref for GuestSlice<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.ptr
}
}
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 mut [T],
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a, T> std::ops::Deref for GuestSliceMut<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<'a, T> std::ops::DerefMut for GuestSliceMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
impl<'a, T> Drop for GuestSliceMut<'a, T> {
fn drop(&mut self) {
self.mem.mut_unborrow(self.borrow)
}
}
pub struct GuestStr<'a> {
ptr: &'a str,
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a> std::ops::Deref for GuestStr<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<'a> Drop for GuestStr<'a> {
fn drop(&mut self) {
self.mem.shared_unborrow(self.borrow)
}
}
pub struct GuestStrMut<'a> {
ptr: &'a mut str,
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a> std::ops::Deref for GuestStrMut<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<'a> std::ops::DerefMut for GuestStrMut<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
impl<'a> Drop for GuestStrMut<'a> {
fn drop(&mut self) {
self.mem.mut_unborrow(self.borrow)
}
}
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)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Trap {
I32Exit(i32),
String(String),
}
impl From<GuestError> for Trap {
fn from(err: GuestError) -> Trap {
Trap::String(err.to_string())
}
}
#[cfg(feature = "wasmtime")]
pub fn run_in_dummy_executor<F: std::future::Future>(
future: F,
) -> Result<F::Output, wasmtime_crate::Trap> {
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 =>
return Err(wasmtime_crate::Trap::new("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);
}
}
}