use crate::{ffi, WasmEdgeResult, WasmValue};
use parking_lot::Mutex;
use std::sync::Arc;
use wasmedge_types::{
error::{GlobalError, WasmEdgeError},
Mutability, ValType,
};
#[derive(Debug)]
pub struct Global {
pub(crate) inner: Arc<Mutex<InnerGlobal>>,
pub(crate) registered: bool,
}
impl Global {
pub fn create(ty: &GlobalType, val: WasmValue) -> WasmEdgeResult<Self> {
let ctx = unsafe { ffi::WasmEdge_GlobalInstanceCreate(ty.inner.0, val.as_raw()) };
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Global(GlobalError::Create))),
false => Ok(Self {
inner: Arc::new(Mutex::new(InnerGlobal(ctx))),
registered: false,
}),
}
}
pub fn ty(&self) -> WasmEdgeResult<GlobalType> {
let ty_ctx = unsafe { ffi::WasmEdge_GlobalInstanceGetGlobalType(self.inner.lock().0) };
match ty_ctx.is_null() {
true => Err(Box::new(WasmEdgeError::Global(GlobalError::Type))),
false => Ok(GlobalType {
inner: InnerGlobalType(ty_ctx as *mut _),
registered: true,
}),
}
}
pub fn get_value(&self) -> WasmValue {
let val = unsafe { ffi::WasmEdge_GlobalInstanceGetValue(self.inner.lock().0) };
val.into()
}
pub fn set_value(&mut self, val: WasmValue) -> WasmEdgeResult<()> {
let ty = self.ty()?;
if ty.mutability() == Mutability::Const {
return Err(Box::new(WasmEdgeError::Global(GlobalError::ModifyConst)));
}
if ty.value_type() != val.ty() {
return Err(Box::new(WasmEdgeError::Global(
GlobalError::UnmatchedValType,
)));
}
unsafe { ffi::WasmEdge_GlobalInstanceSetValue(self.inner.lock().0, val.as_raw()) }
Ok(())
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_ptr(&self) -> *const ffi::WasmEdge_GlobalInstanceContext {
self.inner.lock().0 as *const _
}
}
impl Drop for Global {
fn drop(&mut self) {
if self.registered {
self.inner.lock().0 = std::ptr::null_mut();
} else if Arc::strong_count(&self.inner) == 1 && !self.inner.lock().0.is_null() {
unsafe { ffi::WasmEdge_GlobalInstanceDelete(self.inner.lock().0) };
}
}
}
impl Clone for Global {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
registered: self.registered,
}
}
}
#[derive(Debug)]
pub(crate) struct InnerGlobal(pub(crate) *mut ffi::WasmEdge_GlobalInstanceContext);
unsafe impl Send for InnerGlobal {}
unsafe impl Sync for InnerGlobal {}
#[derive(Debug)]
pub struct GlobalType {
pub(crate) inner: InnerGlobalType,
pub(crate) registered: bool,
}
impl GlobalType {
pub fn create(val_ty: ValType, mutable: Mutability) -> WasmEdgeResult<Self> {
let ctx = unsafe { ffi::WasmEdge_GlobalTypeCreate(val_ty.into(), mutable.into()) };
match ctx.is_null() {
true => Err(Box::new(WasmEdgeError::GlobalTypeCreate)),
false => Ok(Self {
inner: InnerGlobalType(ctx),
registered: false,
}),
}
}
pub fn value_type(&self) -> ValType {
let val = unsafe { ffi::WasmEdge_GlobalTypeGetValType(self.inner.0 as *const _) };
val.into()
}
pub fn mutability(&self) -> Mutability {
let val = unsafe { ffi::WasmEdge_GlobalTypeGetMutability(self.inner.0) };
val.into()
}
#[cfg(feature = "ffi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
pub fn as_ptr(&self) -> *const ffi::WasmEdge_GlobalTypeContext {
self.inner.0 as *const _
}
}
impl Drop for GlobalType {
fn drop(&mut self) {
if !self.registered && !self.inner.0.is_null() {
unsafe { ffi::WasmEdge_GlobalTypeDelete(self.inner.0) };
}
}
}
impl From<wasmedge_types::GlobalType> for GlobalType {
fn from(ty: wasmedge_types::GlobalType) -> Self {
GlobalType::create(ty.value_ty(), ty.mutability()).expect(
"[wasmedge-sys] Failed to convert wasmedge_types::GlobalType into wasmedge_sys::GlobalType.",
)
}
}
impl From<GlobalType> for wasmedge_types::GlobalType {
fn from(ty: GlobalType) -> Self {
wasmedge_types::GlobalType::new(ty.value_type(), ty.mutability())
}
}
#[derive(Debug)]
pub(crate) struct InnerGlobalType(pub(crate) *mut ffi::WasmEdge_GlobalTypeContext);
unsafe impl Send for InnerGlobalType {}
unsafe impl Sync for InnerGlobalType {}
#[cfg(test)]
mod tests {
use super::*;
use std::{
sync::{Arc, Mutex},
thread,
};
use wasmedge_types::{Mutability, ValType};
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_global_type() {
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
assert!(!global_ty.inner.0.is_null());
assert!(!global_ty.registered);
assert_eq!(global_ty.value_type(), ValType::I32);
assert_eq!(global_ty.mutability(), Mutability::Const);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_global_const_i32() {
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
let result = Global::create(&ty, WasmValue::from_i32(99));
assert!(result.is_ok());
let mut global_const = result.unwrap();
assert_eq!(global_const.get_value().to_i32(), 99);
let result = global_const.set_value(WasmValue::from_i32(0));
assert!(result.is_err());
let result = global_const.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
assert!(ty.registered);
assert_eq!(ty.value_type(), ValType::I32);
assert_eq!(ty.mutability(), Mutability::Const);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_global_var_f32() {
let result = GlobalType::create(ValType::F32, Mutability::Var);
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
let result = Global::create(&ty, WasmValue::from_f32(13.14));
assert!(result.is_ok());
let mut global_var = result.unwrap();
assert_eq!(global_var.get_value().to_f32(), 13.14);
let result = global_var.set_value(WasmValue::from_f32(1.314));
assert!(result.is_ok());
assert_eq!(global_var.get_value().to_f32(), 1.314);
let result = global_var.ty();
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
assert!(ty.registered);
assert_eq!(ty.value_type(), ValType::F32);
assert_eq!(ty.mutability(), Mutability::Var);
}
#[test]
#[allow(clippy::assertions_on_result_states)]
fn test_global_conflict() {
{
let result = GlobalType::create(ValType::F32, Mutability::Var);
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
let result = Global::create(&ty, WasmValue::from_i32(520));
assert!(result.is_err());
}
{
let result = GlobalType::create(ValType::F32, Mutability::Var);
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
let result = Global::create(&ty, WasmValue::from_f32(13.14));
assert!(result.is_ok());
let mut global_var = result.unwrap();
let result = global_var.set_value(WasmValue::from_i32(1314));
assert!(result.is_err());
assert_eq!(global_var.get_value().to_f32(), 13.14);
let result = global_var.set_value(WasmValue::from_f32(1.314));
assert!(result.is_ok());
assert_eq!(global_var.get_value().to_f32(), 1.314);
}
}
#[test]
fn test_global_send() {
{
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let handle = thread::spawn(move || {
assert!(!global_ty.inner.0.is_null());
assert!(!global_ty.registered);
assert_eq!(global_ty.value_type(), ValType::I32);
assert_eq!(global_ty.mutability(), Mutability::Const);
});
handle.join().unwrap()
}
{
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let result = Global::create(&global_ty, WasmValue::from_i32(5));
assert!(result.is_ok());
let global = result.unwrap();
let handle = thread::spawn(move || {
assert_eq!(global.get_value().to_i32(), 5);
});
handle.join().unwrap()
}
}
#[test]
fn test_global_sync() {
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let result = Global::create(&global_ty, WasmValue::from_i32(5));
assert!(result.is_ok());
let global = Arc::new(Mutex::new(result.unwrap()));
let global_cloned = Arc::clone(&global);
let handle = thread::spawn(move || {
let result = global_cloned.lock();
assert!(result.is_ok());
let global = result.unwrap();
assert_eq!(global.get_value().to_i32(), 5);
});
handle.join().unwrap()
}
#[test]
fn test_global_clone() {
{
let result = GlobalType::create(ValType::I32, Mutability::Const);
assert!(result.is_ok());
let global_ty = result.unwrap();
let result = Global::create(&global_ty, WasmValue::from_i32(5));
assert!(result.is_ok());
let global = result.unwrap();
let global_cloned = global.clone();
drop(global);
assert_eq!(global_cloned.get_value().to_i32(), 5);
}
{
let result = GlobalType::create(ValType::F32, Mutability::Var);
assert!(result.is_ok());
let ty = result.unwrap();
assert!(!ty.inner.0.is_null());
let result = Global::create(&ty, WasmValue::from_f32(13.14));
assert!(result.is_ok());
let mut global_var = result.unwrap();
assert_eq!(global_var.get_value().to_f32(), 13.14);
let global_var_cloned = global_var.clone();
assert_eq!(
global_var_cloned.get_value().to_f32(),
global_var.get_value().to_f32()
);
let result = global_var.set_value(WasmValue::from_f32(1.314));
assert!(result.is_ok());
assert_eq!(global_var.get_value().to_f32(), 1.314);
drop(global_var);
assert_eq!(global_var_cloned.get_value().to_f32(), 1.314);
}
}
}