wasm3x 0.1.0

Safe, Wasmi/Wasmtime-shaped Rust bindings for the Wasm3 interpreter.
Documentation
//! A compiled-and-validated [`Module`] ready to be instantiated.

use std::sync::Arc;

use wasm3x_sys as ffi;

use crate::engine::Engine;
use crate::error::{Error, Result};

/// A parsed Wasm module that can be instantiated into a [`Store`](crate::Store).
///
/// A [`Module`] retains the Wasm bytes (Wasm3 does not copy them) and re-parses
/// them for each instantiation, so a single [`Module`] can be instantiated into
/// multiple stores.
///
/// # Note
///
/// Wasm3 performs only light structural validation while parsing; it is not a
/// fully validating runtime. Treat [`Module::new`] validation as best-effort.
#[derive(Clone)]
pub struct Module {
    engine: Engine,
    bytes: Arc<[u8]>,
}

impl Module {
    /// Parses and validates a Wasm module from its binary encoding.
    pub fn new(engine: &Engine, wasm: impl AsRef<[u8]>) -> Result<Self> {
        let bytes: Arc<[u8]> = Arc::from(wasm.as_ref());
        // Validate by parsing once, then immediately freeing the parsed module.
        // SAFETY: `bytes` outlives the parse call.
        unsafe {
            let mut raw: ffi::IM3Module = core::ptr::null_mut();
            let result =
                ffi::m3_ParseModule(engine.raw(), &mut raw, bytes.as_ptr(), module_len(&bytes)?);
            Error::from_ffi(result)?;
            ffi::m3_FreeModule(raw);
        }
        Ok(Self {
            engine: engine.clone(),
            bytes,
        })
    }

    /// Returns the [`Engine`] this module belongs to.
    pub fn engine(&self) -> &Engine {
        &self.engine
    }

    pub(crate) fn bytes(&self) -> Arc<[u8]> {
        Arc::clone(&self.bytes)
    }
}

/// Returns the module length as a `u32`, erroring if it does not fit (the
/// Wasm3 C-API takes a 32-bit length).
fn module_len(bytes: &[u8]) -> Result<u32> {
    u32::try_from(bytes.len()).map_err(|_| Error::new("wasm module exceeds 4 GiB"))
}