wasm3x 0.1.0

Safe, Wasmi/Wasmtime-shaped Rust bindings for the Wasm3 interpreter.
Documentation
//! An instantiated [`Module`](crate::Module): the [`Instance`].

use std::ffi::CString;

use wasm3x_sys as ffi;

use crate::error::{Error, Result};
use crate::func::{Func, TypedFunc, WasmParams, WasmResults};
use crate::global::Global;
use crate::memory::Memory;
use crate::store::{AsContext, StoreId, assert_same_store};

/// A handle to an instantiated module within a [`Store`].
///
/// # Note
///
/// Wasm3 looks functions up by name across *all* modules loaded into the
/// runtime (there is no per-instance function namespace and no lookup by
/// index). With one module per store — the common case — this behaves as
/// expected.
///
/// [`Store`]: crate::Store
#[derive(Debug, Clone, Copy)]
pub struct Instance {
    module: ffi::IM3Module,
    store_id: StoreId,
}

impl Instance {
    pub(crate) fn new(module: ffi::IM3Module, store_id: StoreId) -> Self {
        Self { module, store_id }
    }

    pub(crate) fn raw(&self) -> ffi::IM3Module {
        self.module
    }

    /// Looks up an exported function by name.
    ///
    /// **Runtime-global lookup.** Wasm3 resolves functions by name across *all*
    /// modules loaded into the runtime; this does not filter by *this* instance's
    /// module. With one module per store — the intended usage — it behaves as a
    /// per-instance lookup. See the type-level note.
    pub fn get_func(&self, store: impl AsContext, name: &str) -> Option<Func> {
        let ctx = store.as_context();
        assert_same_store(ctx.store_id(), self.store_id);
        Func::find_raw(ctx.runtime(), self.store_id, name)
    }

    /// Looks up an exported function and converts it to a [`TypedFunc`].
    pub fn get_typed_func<Params, Results>(
        &self,
        store: impl AsContext,
        name: &str,
    ) -> Result<TypedFunc<Params, Results>>
    where
        Params: WasmParams,
        Results: WasmResults,
    {
        let ctx = store.as_context();
        assert_same_store(ctx.store_id(), self.store_id);
        let func = Func::find_raw(ctx.runtime(), self.store_id, name)
            .ok_or_else(|| Error::new(format!("exported function `{name}` not found")))?;
        func.typed_checked(self.store_id)
    }

    /// Looks up an exported global by name.
    pub fn get_global(&self, store: impl AsContext, name: &str) -> Option<Global> {
        assert_same_store(store.as_context().store_id(), self.store_id);
        let cname = CString::new(name).ok()?;
        // SAFETY: `module` is owned by the runtime and `cname` is valid.
        let raw = unsafe { ffi::m3_FindGlobal(self.module, cname.as_ptr()) };
        if raw.is_null() {
            return None;
        }
        Some(Global::from_raw(raw, self.store_id))
    }

    /// Returns the runtime's linear memory, if one is present.
    ///
    /// **Runtime-global lookup.** Wasm3 exposes a single linear memory per
    /// runtime and does not resolve memories by export name, so the result is
    /// not filtered by *this* instance's module. With one module per store — the
    /// intended usage — this is the module's memory. See the type-level note.
    pub fn get_memory(&self, store: impl AsContext) -> Option<Memory> {
        let ctx = store.as_context();
        assert_same_store(ctx.store_id(), self.store_id);
        // SAFETY: valid runtime; queries the (single) memory at index 0.
        let mut size = 0u32;
        let ptr = unsafe { ffi::m3_GetMemory(ctx.runtime(), &mut size, 0) };
        if ptr.is_null() {
            return None;
        }
        Some(Memory::new(self.store_id, 0))
    }
}