wasm3x 0.1.0

Safe, Wasmi/Wasmtime-shaped Rust bindings for the Wasm3 interpreter.
Documentation
//! Access to a module's exported [`Global`] variables.

use wasm3x_sys as ffi;

use crate::error::{Error, Result};
use crate::store::{AsContext, AsContextMut, StoreId, assert_same_store};
use crate::value::{GlobalType, Mutability, Val, ValType};

/// A handle to an exported global variable of an [`Instance`](crate::Instance).
#[derive(Debug, Clone, Copy)]
pub struct Global {
    raw: ffi::IM3Global,
    store_id: StoreId,
}

impl Global {
    pub(crate) fn from_raw(raw: ffi::IM3Global, store_id: StoreId) -> Self {
        Self { raw, store_id }
    }

    /// Returns the type (value type and mutability) of this global.
    ///
    /// Fails if the global's value type is not representable through this wrapper.
    pub fn ty(&self, store: impl AsContext) -> Result<GlobalType> {
        assert_same_store(store.as_context().store_id(), self.store_id);
        // SAFETY: `raw` is a valid global handle owned by the store.
        let content = ValType::from_ffi(unsafe { ffi::m3_GetGlobalType(self.raw) })
            .ok_or_else(|| Error::new("global has an unsupported value type"))?;
        // SAFETY: `raw` is a valid global handle owned by the store.
        let mutability = if unsafe { ffi::wasm3x_GetGlobalMutable(self.raw) } {
            Mutability::Mutable
        } else {
            Mutability::Const
        };
        Ok(GlobalType::new(content, mutability))
    }

    /// Reads the current value of this global.
    pub fn get(&self, store: impl AsContext) -> Result<Val> {
        assert_same_store(store.as_context().store_id(), self.store_id);
        // SAFETY: `tagged` is fully initialized by `m3_GetGlobal` on success.
        unsafe {
            let mut tagged: ffi::M3TaggedValue = core::mem::zeroed();
            Error::from_ffi(ffi::m3_GetGlobal(self.raw, &mut tagged))?;
            let ty = ValType::from_ffi(tagged.type_)
                .ok_or_else(|| Error::new("global has an unsupported value type"))?;
            Ok(match ty {
                ValType::I32 => Val::I32(tagged.value.i32_ as i32),
                ValType::I64 => Val::I64(tagged.value.i64_ as i64),
                ValType::F32 => Val::F32(tagged.value.f32_),
                ValType::F64 => Val::F64(tagged.value.f64_),
            })
        }
    }

    /// Writes a new value into this global.
    ///
    /// Fails if the global is immutable or the value type does not match.
    pub fn set(&self, mut store: impl AsContextMut, value: Val) -> Result<()> {
        assert_same_store(store.as_context_mut().store_id(), self.store_id);
        let mut tagged = ffi::M3TaggedValue {
            type_: value.ty().to_ffi(),
            value: match value {
                Val::I32(v) => ffi::M3TaggedValue_M3ValueUnion { i32_: v as u32 },
                Val::I64(v) => ffi::M3TaggedValue_M3ValueUnion { i64_: v as u64 },
                Val::F32(v) => ffi::M3TaggedValue_M3ValueUnion { f32_: v },
                Val::F64(v) => ffi::M3TaggedValue_M3ValueUnion { f64_: v },
            },
        };
        // SAFETY: `tagged` is fully initialized; `raw` is a valid global.
        Error::from_ffi(unsafe { ffi::m3_SetGlobal(self.raw, &mut tagged) })
    }
}