mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 21:58:20 +00:00
Added support for Globals
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use crate::instance::Instance;
|
||||
use crate::wasm_bindgen_polyfill::Global;
|
||||
use crate::WasmerEnv;
|
||||
use js_sys::Function;
|
||||
use js_sys::WebAssembly::{Memory, Table};
|
||||
@@ -7,7 +8,7 @@ use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasmer_types::{ExternType, FunctionType, MemoryType, TableType};
|
||||
use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VMMemory {
|
||||
@@ -21,6 +22,18 @@ impl VMMemory {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VMGlobal {
|
||||
pub(crate) global: Global,
|
||||
pub(crate) ty: GlobalType,
|
||||
}
|
||||
|
||||
impl VMGlobal {
|
||||
pub(crate) fn new(global: Global, ty: GlobalType) -> Self {
|
||||
Self { global, ty }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VMTable {
|
||||
pub(crate) table: Table,
|
||||
@@ -85,8 +98,9 @@ pub enum Export {
|
||||
|
||||
/// A memory export value.
|
||||
Memory(VMMemory),
|
||||
// /// A global export value.
|
||||
// Global(VMGlobal),
|
||||
|
||||
/// A global export value.
|
||||
Global(VMGlobal),
|
||||
}
|
||||
|
||||
impl Export {
|
||||
@@ -95,6 +109,7 @@ impl Export {
|
||||
Export::Memory(js_wasm_memory) => js_wasm_memory.memory.as_ref(),
|
||||
Export::Function(js_func) => js_func.function.as_ref(),
|
||||
Export::Table(js_wasm_table) => js_wasm_table.table.as_ref(),
|
||||
Export::Global(js_wasm_global) => js_wasm_global.global.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +127,16 @@ impl From<(JsValue, ExternType)> for Export {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
ExternType::Global(global_type) => {
|
||||
if val.is_instance_of::<Global>() {
|
||||
return Export::Global(VMGlobal::new(
|
||||
val.unchecked_into::<Global>(),
|
||||
global_type,
|
||||
));
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
ExternType::Function(function_type) => {
|
||||
if val.is_instance_of::<Function>() {
|
||||
return Export::Function(VMFunction::new(
|
||||
|
||||
100
lib/js-api/src/externals/global.rs
vendored
100
lib/js-api/src/externals/global.rs
vendored
@@ -1,14 +1,16 @@
|
||||
use crate::export::Export;
|
||||
use crate::export::VMGlobal;
|
||||
use crate::exports::{ExportError, Exportable};
|
||||
use crate::externals::Extern;
|
||||
use crate::store::{Store, StoreObject};
|
||||
use crate::types::Val;
|
||||
use crate::types::{Val, ValType};
|
||||
use crate::wasm_bindgen_polyfill::Global as JSGlobal;
|
||||
use crate::GlobalType;
|
||||
use crate::Mutability;
|
||||
use crate::RuntimeError;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasmer_engine::Export;
|
||||
use wasmer_vm::{Global as RuntimeGlobal, VMGlobal};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
/// A WebAssembly `global` instance.
|
||||
///
|
||||
@@ -16,7 +18,10 @@ use wasmer_vm::{Global as RuntimeGlobal, VMGlobal};
|
||||
/// It consists of an individual value and a flag indicating whether it is mutable.
|
||||
///
|
||||
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Global {
|
||||
store: Store,
|
||||
vm_global: VMGlobal,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
@@ -56,25 +61,33 @@ impl Global {
|
||||
|
||||
/// Create a `Global` with the initial value [`Val`] and the provided [`Mutability`].
|
||||
fn from_value(store: &Store, val: Val, mutability: Mutability) -> Result<Self, RuntimeError> {
|
||||
if !val.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` globals are not supported"));
|
||||
}
|
||||
let global = RuntimeGlobal::new(GlobalType {
|
||||
let global_ty = GlobalType {
|
||||
mutability,
|
||||
ty: val.ty(),
|
||||
});
|
||||
unsafe {
|
||||
global
|
||||
.set_unchecked(val.clone())
|
||||
.map_err(|e| RuntimeError::new(format!("create global for {:?}: {}", val, e)))?;
|
||||
};
|
||||
let descriptor = js_sys::Object::new();
|
||||
let (type_str, value) = match val {
|
||||
Val::I32(i) => ("i32", JsValue::from_f64(i as _)),
|
||||
Val::I64(i) => ("i64", JsValue::from_f64(i as _)),
|
||||
Val::F32(f) => ("f32", JsValue::from_f64(f as _)),
|
||||
Val::F64(f) => ("f64", JsValue::from_f64(f)),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
};
|
||||
// This is the value type as string, even though is incorrectly called "value"
|
||||
// in the JS API.
|
||||
js_sys::Reflect::set(&descriptor, &"value".into(), &type_str.into());
|
||||
js_sys::Reflect::set(
|
||||
&descriptor,
|
||||
&"mutable".into(),
|
||||
&mutability.is_mutable().into(),
|
||||
);
|
||||
|
||||
let js_global = JSGlobal::new(&descriptor, &value).unwrap();
|
||||
let global = VMGlobal::new(js_global, global_ty);
|
||||
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
vm_global: VMGlobal {
|
||||
from: Arc::new(global),
|
||||
instance_ref: None,
|
||||
},
|
||||
vm_global: global,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,7 +106,7 @@ impl Global {
|
||||
/// assert_eq!(v.ty(), &GlobalType::new(Type::I64, Mutability::Var));
|
||||
/// ```
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
self.vm_global.from.ty()
|
||||
&self.vm_global.ty
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Global` belongs.
|
||||
@@ -125,7 +138,13 @@ impl Global {
|
||||
/// assert_eq!(g.get(), Value::I32(1));
|
||||
/// ```
|
||||
pub fn get(&self) -> Val {
|
||||
self.vm_global.from.get(&self.store)
|
||||
match self.vm_global.ty.ty {
|
||||
ValType::I32 => Val::I32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::I64 => Val::I64(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F32 => Val::F32(self.vm_global.global.value().as_f64().unwrap() as _),
|
||||
ValType::F64 => Val::F64(self.vm_global.global.value().as_f64().unwrap()),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a custom value [`Val`] to the runtime Global.
|
||||
@@ -170,15 +189,20 @@ impl Global {
|
||||
/// g.set(Value::I64(2)).unwrap();
|
||||
/// ```
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if !val.comes_from_same_store(&self.store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
if self.vm_global.ty.mutability == Mutability::Const {
|
||||
return Err(RuntimeError::from_str("The global is immutable"));
|
||||
}
|
||||
unsafe {
|
||||
self.vm_global
|
||||
.from
|
||||
.set(val)
|
||||
.map_err(|e| RuntimeError::new(format!("{}", e)))?;
|
||||
if val.ty() != self.vm_global.ty.ty {
|
||||
return Err(RuntimeError::from_str("The types don't match"));
|
||||
}
|
||||
let new_value = match val {
|
||||
Val::I32(i) => JsValue::from_f64(i as _),
|
||||
Val::I64(i) => JsValue::from_f64(i as _),
|
||||
Val::F32(f) => JsValue::from_f64(f as _),
|
||||
Val::F64(f) => JsValue::from_f64(f),
|
||||
_ => unimplemented!("The type is not yet supported in the JS Global API"),
|
||||
};
|
||||
self.vm_global.global.set_value(&new_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -202,35 +226,13 @@ impl Global {
|
||||
/// assert!(g.same(&g));
|
||||
/// ```
|
||||
pub fn same(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.vm_global.from, &other.vm_global.from)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Global {
|
||||
fn clone(&self) -> Self {
|
||||
let mut vm_global = self.vm_global.clone();
|
||||
vm_global.upgrade_instance_ref().unwrap();
|
||||
|
||||
Self {
|
||||
store: self.store.clone(),
|
||||
vm_global,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Global {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("Global")
|
||||
.field("ty", &self.ty())
|
||||
.field("value", &self.get())
|
||||
.finish()
|
||||
self.vm_global == other.vm_global
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Global {
|
||||
fn to_export(&self) -> Export {
|
||||
self.vm_global.clone().into()
|
||||
Export::Global(self.vm_global.clone())
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
|
||||
3
lib/js-api/src/externals/memory.rs
vendored
3
lib/js-api/src/externals/memory.rs
vendored
@@ -309,8 +309,7 @@ impl Memory {
|
||||
|
||||
impl<'a> Exportable<'a> for Memory {
|
||||
fn to_export(&self) -> Export {
|
||||
unimplemented!();
|
||||
// self.vm_memory.clone().into()
|
||||
Export::Memory(self.vm_memory.clone())
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
|
||||
26
lib/js-api/src/externals/mod.rs
vendored
26
lib/js-api/src/externals/mod.rs
vendored
@@ -1,5 +1,5 @@
|
||||
pub(crate) mod function;
|
||||
// mod global;
|
||||
mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
|
||||
@@ -7,7 +7,7 @@ pub use self::function::{
|
||||
FromToNativeWasmType, Function, HostFunction, WasmTypeList, WithEnv, WithoutEnv,
|
||||
};
|
||||
|
||||
// pub use self::global::Global;
|
||||
pub use self::global::Global;
|
||||
pub use self::memory::Memory;
|
||||
pub use self::table::Table;
|
||||
|
||||
@@ -25,8 +25,8 @@ use std::fmt;
|
||||
pub enum Extern {
|
||||
/// A external [`Function`].
|
||||
Function(Function),
|
||||
// /// A external [`Global`].
|
||||
// Global(Global),
|
||||
/// A external [`Global`].
|
||||
Global(Global),
|
||||
/// A external [`Table`].
|
||||
Table(Table),
|
||||
/// A external [`Memory`].
|
||||
@@ -40,7 +40,7 @@ impl Extern {
|
||||
Self::Function(ft) => ExternType::Function(ft.ty().clone()),
|
||||
Self::Memory(ft) => ExternType::Memory(ft.ty()),
|
||||
Self::Table(tt) => ExternType::Table(*tt.ty()),
|
||||
// Self::Global(gt) => ExternType::Global(*gt.ty()),
|
||||
Self::Global(gt) => ExternType::Global(*gt.ty()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Extern {
|
||||
match export {
|
||||
Export::Function(f) => Self::Function(Function::from_vm_export(store, f)),
|
||||
Export::Memory(m) => Self::Memory(Memory::from_vm_export(store, m)),
|
||||
// Export::Global(g) => Self::Global(Global::from_vm_export(store, g)),
|
||||
Export::Global(g) => Self::Global(Global::from_vm_export(store, g)),
|
||||
Export::Table(t) => Self::Table(Table::from_vm_export(store, t)),
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl<'a> Exportable<'a> for Extern {
|
||||
fn to_export(&self) -> Export {
|
||||
match self {
|
||||
Self::Function(f) => f.to_export(),
|
||||
// Self::Global(g) => g.to_export(),
|
||||
Self::Global(g) => g.to_export(),
|
||||
Self::Memory(m) => m.to_export(),
|
||||
Self::Table(t) => t.to_export(),
|
||||
}
|
||||
@@ -80,7 +80,7 @@ impl fmt::Debug for Extern {
|
||||
"{}",
|
||||
match self {
|
||||
Self::Function(_) => "Function(...)",
|
||||
// Self::Global(_) => "Global(...)",
|
||||
Self::Global(_) => "Global(...)",
|
||||
Self::Memory(_) => "Memory(...)",
|
||||
Self::Table(_) => "Table(...)",
|
||||
}
|
||||
@@ -94,11 +94,11 @@ impl From<Function> for Extern {
|
||||
}
|
||||
}
|
||||
|
||||
// impl From<Global> for Extern {
|
||||
// fn from(r: Global) -> Self {
|
||||
// Self::Global(r)
|
||||
// }
|
||||
// }
|
||||
impl From<Global> for Extern {
|
||||
fn from(r: Global) -> Self {
|
||||
Self::Global(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Memory> for Extern {
|
||||
fn from(r: Memory) -> Self {
|
||||
|
||||
@@ -294,6 +294,7 @@ mod module;
|
||||
#[cfg(feature = "wasm-types-polyfill")]
|
||||
mod module_info_polyfill;
|
||||
mod resolver;
|
||||
mod wasm_bindgen_polyfill;
|
||||
// mod native;
|
||||
mod ptr;
|
||||
mod store;
|
||||
@@ -309,9 +310,7 @@ pub use crate::cell::WasmCell;
|
||||
pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv};
|
||||
pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator};
|
||||
pub use crate::externals::{
|
||||
Extern, FromToNativeWasmType, Function, HostFunction, Memory, Table,
|
||||
/* Global, */
|
||||
WasmTypeList,
|
||||
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
|
||||
};
|
||||
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
|
||||
pub use crate::instance::{Instance, InstantiationError};
|
||||
|
||||
32
lib/js-api/src/wasm_bindgen_polyfill.rs
Normal file
32
lib/js-api/src/wasm_bindgen_polyfill.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use js_sys::Object;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
// WebAssembly.Global
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
/// The `WebAssembly.Global()` constructor creates a new `Global` object
|
||||
/// of the given type and value.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global)
|
||||
#[wasm_bindgen(js_namespace = WebAssembly, extends = Object, typescript_type = "WebAssembly.Global")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub type Global;
|
||||
|
||||
/// The `WebAssembly.Global()` constructor creates a new `Global` object
|
||||
/// of the given type and value.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global)
|
||||
#[wasm_bindgen(constructor, js_namespace = WebAssembly, catch)]
|
||||
pub fn new(global_descriptor: &Object, value: &JsValue) -> Result<Global, JsValue>;
|
||||
|
||||
/// The value prototype property of the `WebAssembly.Global` object
|
||||
/// returns the value of the global.
|
||||
///
|
||||
/// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global)
|
||||
#[wasm_bindgen(method, getter, structural, js_namespace = WebAssembly)]
|
||||
pub fn value(this: &Global) -> JsValue;
|
||||
|
||||
#[wasm_bindgen(method, setter = value, structural, js_namespace = WebAssembly)]
|
||||
pub fn set_value(this: &Global, value: &JsValue);
|
||||
}
|
||||
@@ -2,62 +2,56 @@ use wasm_bindgen_test::*;
|
||||
// use anyhow::Result;
|
||||
use wasmer_js::*;
|
||||
|
||||
// #[test]
|
||||
// fn global_new() -> Result<()> {
|
||||
// let store = Store::default();
|
||||
// let global = Global::new(&store, Value::I32(10));
|
||||
// assert_eq!(
|
||||
// *global.ty(),
|
||||
// GlobalType {
|
||||
// ty: Type::I32,
|
||||
// mutability: Mutability::Const
|
||||
// }
|
||||
// );
|
||||
#[wasm_bindgen_test]
|
||||
fn global_new() {
|
||||
let store = Store::default();
|
||||
let global = Global::new(&store, Value::I32(10));
|
||||
assert_eq!(
|
||||
*global.ty(),
|
||||
GlobalType {
|
||||
ty: Type::I32,
|
||||
mutability: Mutability::Const
|
||||
}
|
||||
);
|
||||
|
||||
// let global_mut = Global::new_mut(&store, Value::I32(10));
|
||||
// assert_eq!(
|
||||
// *global_mut.ty(),
|
||||
// GlobalType {
|
||||
// ty: Type::I32,
|
||||
// mutability: Mutability::Var
|
||||
// }
|
||||
// );
|
||||
let global_mut = Global::new_mut(&store, Value::I32(10));
|
||||
assert_eq!(
|
||||
*global_mut.ty(),
|
||||
GlobalType {
|
||||
ty: Type::I32,
|
||||
mutability: Mutability::Var
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
#[wasm_bindgen_test]
|
||||
fn global_get() {
|
||||
let store = Store::default();
|
||||
let global_i32 = Global::new(&store, Value::I32(10));
|
||||
assert_eq!(global_i32.get(), Value::I32(10));
|
||||
// let global_i64 = Global::new(&store, Value::I64(20));
|
||||
// assert_eq!(global_i64.get(), Value::I64(20));
|
||||
let global_f32 = Global::new(&store, Value::F32(10.0));
|
||||
assert_eq!(global_f32.get(), Value::F32(10.0));
|
||||
// let global_f64 = Global::new(&store, Value::F64(20.0));
|
||||
// assert_eq!(global_f64.get(), Value::F64(20.0));
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn global_get() -> Result<()> {
|
||||
// let store = Store::default();
|
||||
// let global_i32 = Global::new(&store, Value::I32(10));
|
||||
// assert_eq!(global_i32.get(), Value::I32(10));
|
||||
// let global_i64 = Global::new(&store, Value::I64(20));
|
||||
// assert_eq!(global_i64.get(), Value::I64(20));
|
||||
// let global_f32 = Global::new(&store, Value::F32(10.0));
|
||||
// assert_eq!(global_f32.get(), Value::F32(10.0));
|
||||
// let global_f64 = Global::new(&store, Value::F64(20.0));
|
||||
// assert_eq!(global_f64.get(), Value::F64(20.0));
|
||||
#[wasm_bindgen_test]
|
||||
fn global_set() {
|
||||
let store = Store::default();
|
||||
let global_i32 = Global::new(&store, Value::I32(10));
|
||||
// Set on a constant should error
|
||||
assert!(global_i32.set(Value::I32(20)).is_err());
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
let global_i32_mut = Global::new_mut(&store, Value::I32(10));
|
||||
// Set on different type should error
|
||||
assert!(global_i32_mut.set(Value::I64(20)).is_err());
|
||||
|
||||
// #[test]
|
||||
// fn global_set() -> Result<()> {
|
||||
// let store = Store::default();
|
||||
// let global_i32 = Global::new(&store, Value::I32(10));
|
||||
// // Set on a constant should error
|
||||
// assert!(global_i32.set(Value::I32(20)).is_err());
|
||||
|
||||
// let global_i32_mut = Global::new_mut(&store, Value::I32(10));
|
||||
// // Set on different type should error
|
||||
// assert!(global_i32_mut.set(Value::I64(20)).is_err());
|
||||
|
||||
// // Set on same type should succeed
|
||||
// global_i32_mut.set(Value::I32(20))?;
|
||||
// assert_eq!(global_i32_mut.get(), Value::I32(20));
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
// Set on same type should succeed
|
||||
global_i32_mut.set(Value::I32(20)).unwrap();
|
||||
assert_eq!(global_i32_mut.get(), Value::I32(20));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn table_new() {
|
||||
|
||||
@@ -290,3 +290,57 @@ fn test_imported_function_native_with_wasmer_env() {
|
||||
let expected = vec![Val::I32(36)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_imported_exported_global() {
|
||||
let store = Store::default();
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
(global $mut_i32_import (import "" "global") (mut i32))
|
||||
(func (export "getGlobal") (result i32) (global.get $mut_i32_import))
|
||||
(func (export "incGlobal") (global.set $mut_i32_import (
|
||||
i32.add (i32.const 1) (global.get $mut_i32_import)
|
||||
)))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![ExternType::Global(GlobalType::new(
|
||||
ValType::I32,
|
||||
Mutability::Var,
|
||||
))],
|
||||
exports: vec![
|
||||
ExternType::Function(FunctionType::new(vec![], vec![Type::I32])),
|
||||
ExternType::Function(FunctionType::new(vec![], vec![])),
|
||||
],
|
||||
});
|
||||
let mut global = Global::new_mut(&store, Value::I32(0));
|
||||
let import_object = imports! {
|
||||
"" => {
|
||||
"global" => global.clone()
|
||||
}
|
||||
};
|
||||
let instance = Instance::new(&module, &import_object).unwrap();
|
||||
|
||||
let get_global = instance.exports.get_function("getGlobal").unwrap();
|
||||
assert_eq!(
|
||||
get_global.call(&[]),
|
||||
Ok(vec![Val::I32(0)].into_boxed_slice())
|
||||
);
|
||||
|
||||
global.set(Value::I32(42));
|
||||
assert_eq!(
|
||||
get_global.call(&[]),
|
||||
Ok(vec![Val::I32(42)].into_boxed_slice())
|
||||
);
|
||||
|
||||
let inc_global = instance.exports.get_function("incGlobal").unwrap();
|
||||
inc_global.call(&[]);
|
||||
assert_eq!(
|
||||
get_global.call(&[]),
|
||||
Ok(vec![Val::I32(43)].into_boxed_slice())
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user