1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
use crate::borrow::BorrowChecker;
use crate::{BorrowHandle, GuestError, GuestMemory, Region};
use std::cell::UnsafeCell;
/// Lightweight `wasmtime::Memory` wrapper so we can implement the
/// `wiggle::GuestMemory` trait on it.
pub struct WasmtimeGuestMemory<'a> {
mem: &'a [UnsafeCell<u8>],
bc: BorrowChecker,
shared: bool,
}
// These need to be reapplied due to the usage of `UnsafeCell` internally.
unsafe impl Send for WasmtimeGuestMemory<'_> {}
unsafe impl Sync for WasmtimeGuestMemory<'_> {}
impl<'a> WasmtimeGuestMemory<'a> {
pub fn new(mem: &'a mut [u8]) -> Self {
Self {
// SAFETY: here the `&mut [u8]` is casted to `&[UnsafeCell<u8>]`
// which is losing in effect the `&mut` access but retaining the
// borrow. This is done to reflect how the memory is not safe to
// access while multiple borrows are handed out internally, checked
// with `bc` below.
//
// Additionally this allows unshared memories to have the same
// internal representation as shared memories.
mem: unsafe { std::slice::from_raw_parts(mem.as_ptr().cast(), mem.len()) },
// Wiggle does not expose any methods for functions to re-enter
// the WebAssembly instance, or expose the memory via non-wiggle
// mechanisms. However, the user-defined code may end up
// re-entering the instance, in which case this is an incorrect
// implementation - we require exactly one BorrowChecker exist per
// instance.
// This BorrowChecker construction is a holdover until it is
// integrated fully with wasmtime:
// https://github.com/bytecodealliance/wasmtime/issues/1917
bc: BorrowChecker::new(),
shared: false,
}
}
pub fn shared(mem: &'a [UnsafeCell<u8>]) -> Self {
Self {
mem,
bc: BorrowChecker::new(),
shared: true,
}
}
}
unsafe impl GuestMemory for WasmtimeGuestMemory<'_> {
#[inline]
fn base(&self) -> &[UnsafeCell<u8>] {
self.mem
}
// Note that this implementation has special cases for shared memory
// specifically because no regions of a shared memory can ever be borrowed.
// In the shared memory cases `shared_borrow` and `mut_borrow` are never
// called so that can be used to optimize the other methods by quickly
// checking a flag before calling the more expensive borrow-checker methods.
#[inline]
fn can_read(&self, r: Region) -> bool {
self.shared || self.bc.can_read(r)
}
#[inline]
fn can_write(&self, r: Region) -> bool {
self.shared || self.bc.can_write(r)
}
#[inline]
fn shared_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
debug_assert!(!self.shared);
self.bc.shared_borrow(r)
}
#[inline]
fn mut_borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
debug_assert!(!self.shared);
self.bc.mut_borrow(r)
}
#[inline]
fn shared_unborrow(&self, h: BorrowHandle) {
debug_assert!(!self.shared);
self.bc.shared_unborrow(h)
}
#[inline]
fn mut_unborrow(&self, h: BorrowHandle) {
debug_assert!(!self.shared);
self.bc.mut_unborrow(h)
}
#[inline]
fn is_shared_memory(&self) -> bool {
self.shared
}
}