fizzyx 0.1.1

Safe, ergonomic Rust bindings for the Fizzy WebAssembly interpreter.
Documentation
//! Access to an instance's exported globals.

use crate::error::{Error, Result};
use crate::instance::Instance;
use crate::value::{GlobalType, Mutability, Val};
use fizzyx_sys as sys;
use std::ffi::CString;
use std::mem::MaybeUninit;

/// A handle to a global exported by an [`Instance`].
///
/// Obtained via [`Instance::get_global`]. Like [`Func`](crate::Func) and
/// [`Memory`](crate::Memory), a `Global` is an owned handle: it stores the
/// export name and type and is passed the owning instance for each access.
#[derive(Debug, Clone)]
pub struct Global {
    name: CString,
    ty: GlobalType,
}

impl Global {
    pub(crate) fn new(name: CString, ty: GlobalType) -> Self {
        Self { name, ty }
    }

    /// Returns the type of the global.
    pub fn ty(&self) -> GlobalType {
        self.ty
    }

    /// Returns the current value of the global from `instance`.
    pub fn get(&self, instance: &Instance) -> Val {
        let value = self.resolve(instance);
        // SAFETY: `value` points at the instance's live storage for this global.
        let raw = unsafe { *value };
        Val::from_sys(raw, self.ty.content())
    }

    /// Sets the value of the global in `instance`.
    ///
    /// # Errors
    ///
    /// Returns [`Error::GlobalImmutable`] if the global is not mutable, or
    /// [`Error::TypeMismatch`] if `value` has a different type than the global.
    pub fn set(&self, instance: &mut Instance, value: Val) -> Result<()> {
        if self.ty.mutability() != Mutability::Mutable {
            return Err(Error::GlobalImmutable);
        }
        if value.ty() != self.ty.content() {
            return Err(Error::TypeMismatch {
                index: 0,
                expected: self.ty.content().name(),
                found: value.ty().name(),
            });
        }
        let ptr = self.resolve(instance);
        // SAFETY: `ptr` points at the instance's live storage for this global and
        // the value type has been checked to match.
        unsafe { *ptr = value.to_sys() };
        Ok(())
    }

    /// Re-resolves the pointer to the global's value within `instance`.
    ///
    /// The global was confirmed to exist when this handle was created and Fizzy
    /// never removes exports, so the lookup always succeeds for the originating
    /// instance.
    fn resolve(&self, instance: &Instance) -> *mut sys::FizzyValue {
        let mut out = MaybeUninit::<sys::FizzyExternalGlobal>::uninit();
        // SAFETY: valid instance and NUL-terminated name; `out` is a valid
        // out-pointer that Fizzy fills iff it returns `true`.
        let found = unsafe {
            sys::fizzy_find_exported_global(instance.as_ptr(), self.name.as_ptr(), out.as_mut_ptr())
        };
        debug_assert!(found, "exported global `{:?}` disappeared", self.name);
        // SAFETY: populated by the successful lookup above.
        unsafe { out.assume_init() }.value
    }
}