wasm3x 0.1.0

Safe, Wasmi/Wasmtime-shaped Rust bindings for the Wasm3 interpreter.
Documentation
//! The [`Caller`] handed to host functions during a call.

use core::marker::PhantomData;

use wasm3x_sys as ffi;

use crate::memory::Memory;
use crate::store::{AsContext, AsContextMut, StoreContext, StoreContextMut, StoreId};
use crate::trampoline::RawCaller;

/// The context passed as the first argument to every host function.
///
/// A [`Caller`] gives a host function access to the store's host `data` (as
/// `&T`/`&mut T`) and to the runtime's linear [`Memory`], for the duration of
/// the call. It implements [`AsContext`]/[`AsContextMut`], so handle methods
/// such as [`Memory::read`]/[`Memory::write`] accept `&caller`/`&mut caller`
/// exactly like they accept a [`Store`](crate::Store).
///
/// # Re-entrancy
///
/// Unlike Wasmtime, a [`Caller`] deliberately offers **no** way to call back
/// into Wasm. Wasm3's public entry points (`m3_Call`, ...) always execute from
/// the base of the runtime stack, which would corrupt the suspended outer
/// frame. Because a [`Caller`] never yields a `&mut Store`, and host closures
/// are `'static` and cannot capture the borrowed store, re-entering the same
/// store from a host function is a compile-time impossibility:
///
/// ```compile_fail
/// use wasm3x::{Caller, Engine, Linker, Store};
/// let engine = Engine::default();
/// let mut store = Store::new(&engine, ());
/// let mut linker = Linker::<()>::new(&engine);
/// // A host closure must be `Send + Sync + 'static`; a `Store` is neither and
/// // cannot be captured, so there is no way to call back into it.
/// linker
///     .func_wrap("env", "f", move |_c: Caller<'_, ()>| {
///         let _smuggled = store; // capture the store to re-enter it -> rejected
///         0i32
///     })
///     .unwrap();
/// ```
pub struct Caller<'a, T> {
    runtime: ffi::IM3Runtime,
    store_id: StoreId,
    /// Points at the owning `Store`'s host `data`; valid for the call duration.
    data: *mut T,
    _marker: PhantomData<&'a mut T>,
}

impl<'a, T> Caller<'a, T> {
    /// Reconstructs a typed [`Caller`] from the type-erased bundle the
    /// trampoline builds. `T` is known here because the linking closure that
    /// calls this was monomorphized by `Linker<T>`.
    pub(crate) fn from_raw(raw: RawCaller) -> Self {
        Self {
            runtime: raw.runtime,
            store_id: raw.store_id,
            data: raw.data as *mut T,
            _marker: PhantomData,
        }
    }

    /// Returns a shared reference to the host data.
    pub fn data(&self) -> &T {
        // SAFETY: `data` points at the owning store's `data`, which outlives
        // this call, and no `&mut` alias exists while the call is in flight.
        unsafe { &*self.data }
    }

    /// Returns an exclusive reference to the host data.
    pub fn data_mut(&mut self) -> &mut T {
        // SAFETY: as `data`, plus the `&mut self` receiver guarantees no other
        // borrow of the data is live.
        unsafe { &mut *self.data }
    }

    /// Returns the runtime's linear memory, if one is present.
    ///
    /// Wasm3 exposes a single linear memory per runtime, so this ignores any
    /// export name (mirroring [`Instance::get_memory`](crate::Instance::get_memory)).
    pub fn get_memory(&self) -> Option<Memory> {
        let mut size = 0u32;
        // SAFETY: valid runtime; queries the (single) memory at index 0.
        let ptr = unsafe { ffi::m3_GetMemory(self.runtime, &mut size, 0) };
        if ptr.is_null() {
            return None;
        }
        Some(Memory::new(self.store_id, 0))
    }
}

impl<'a, T> AsContext for Caller<'a, T> {
    type Data = T;
    fn as_context(&self) -> StoreContext<'_, T> {
        // SAFETY: see `Caller::data`.
        StoreContext::new(self.runtime, self.store_id, unsafe { &*self.data })
    }
}

impl<'a, T> AsContextMut for Caller<'a, T> {
    fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
        // SAFETY: see `Caller::data_mut`.
        StoreContextMut::new(self.runtime, self.store_id, unsafe { &mut *self.data })
    }
}