wasmtime: Implement global.{get,set} for externref globals (#1969)
* wasmtime: Implement `global.{get,set}` for externref globals
We use libcalls to implement these -- unlike `table.{get,set}`, for which we
create inline JIT fast paths -- because no known toolchain actually uses
externref globals.
Part of #929
* wasmtime: Enable `{extern,func}ref` globals in the API
This commit is contained in:
6
build.rs
6
build.rs
@@ -204,11 +204,7 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
|
|||||||
("simd", "simd_splat") => return true, // FIXME Unsupported feature: proposed SIMD operator I32x4TruncSatF32x4S
|
("simd", "simd_splat") => return true, // FIXME Unsupported feature: proposed SIMD operator I32x4TruncSatF32x4S
|
||||||
|
|
||||||
// Still working on implementing these. See #929.
|
// Still working on implementing these. See #929.
|
||||||
("reference_types", "global")
|
("reference_types", "table_fill") => {
|
||||||
| ("reference_types", "linking")
|
|
||||||
| ("reference_types", "ref_func")
|
|
||||||
| ("reference_types", "ref_null")
|
|
||||||
| ("reference_types", "table_fill") => {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,6 +175,10 @@ declare_builtin_functions! {
|
|||||||
/// Returns an index to do a GC and then insert a `VMExternRef` into the
|
/// Returns an index to do a GC and then insert a `VMExternRef` into the
|
||||||
/// `VMExternRefActivationsTable`.
|
/// `VMExternRefActivationsTable`.
|
||||||
activations_table_insert_with_gc(vmctx, reference) -> ();
|
activations_table_insert_with_gc(vmctx, reference) -> ();
|
||||||
|
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
|
||||||
|
externref_global_get(vmctx, i32) -> (reference);
|
||||||
|
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
|
||||||
|
externref_global_set(vmctx, i32, reference) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinFunctionIndex {
|
impl BuiltinFunctionIndex {
|
||||||
@@ -432,6 +436,28 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
|
|
||||||
new_ref_count
|
new_ref_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_global_location(
|
||||||
|
&mut self,
|
||||||
|
func: &mut ir::Function,
|
||||||
|
index: GlobalIndex,
|
||||||
|
) -> (ir::GlobalValue, i32) {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
if let Some(def_index) = self.module.defined_global_index(index) {
|
||||||
|
let offset = i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
|
||||||
|
(vmctx, offset)
|
||||||
|
} else {
|
||||||
|
let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
|
||||||
|
let global = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::try_from(from_offset).unwrap()),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
(global, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
|
// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
|
||||||
@@ -1042,19 +1068,56 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
fn translate_custom_global_get(
|
fn translate_custom_global_get(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: cranelift_codegen::cursor::FuncCursor<'_>,
|
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
|
||||||
_: cranelift_wasm::GlobalIndex,
|
index: cranelift_wasm::GlobalIndex,
|
||||||
) -> WasmResult<ir::Value> {
|
) -> WasmResult<ir::Value> {
|
||||||
unreachable!("we don't make any custom globals")
|
debug_assert_eq!(
|
||||||
|
self.module.globals[index].wasm_ty,
|
||||||
|
WasmType::ExternRef,
|
||||||
|
"We only use GlobalVariable::Custom for externref"
|
||||||
|
);
|
||||||
|
|
||||||
|
let builtin_index = BuiltinFunctionIndex::externref_global_get();
|
||||||
|
let builtin_sig = self
|
||||||
|
.builtin_function_signatures
|
||||||
|
.externref_global_get(&mut pos.func);
|
||||||
|
|
||||||
|
let (vmctx, builtin_addr) =
|
||||||
|
self.translate_load_builtin_function_address(&mut pos, builtin_index);
|
||||||
|
|
||||||
|
let global_index_arg = pos.ins().iconst(I32, index.as_u32() as i64);
|
||||||
|
let call_inst =
|
||||||
|
pos.ins()
|
||||||
|
.call_indirect(builtin_sig, builtin_addr, &[vmctx, global_index_arg]);
|
||||||
|
|
||||||
|
Ok(pos.func.dfg.first_result(call_inst))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_custom_global_set(
|
fn translate_custom_global_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: cranelift_codegen::cursor::FuncCursor<'_>,
|
mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
|
||||||
_: cranelift_wasm::GlobalIndex,
|
index: cranelift_wasm::GlobalIndex,
|
||||||
_: ir::Value,
|
value: ir::Value,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
unreachable!("we don't make any custom globals")
|
debug_assert_eq!(
|
||||||
|
self.module.globals[index].wasm_ty,
|
||||||
|
WasmType::ExternRef,
|
||||||
|
"We only use GlobalVariable::Custom for externref"
|
||||||
|
);
|
||||||
|
|
||||||
|
let builtin_index = BuiltinFunctionIndex::externref_global_set();
|
||||||
|
let builtin_sig = self
|
||||||
|
.builtin_function_signatures
|
||||||
|
.externref_global_set(&mut pos.func);
|
||||||
|
|
||||||
|
let (vmctx, builtin_addr) =
|
||||||
|
self.translate_load_builtin_function_address(&mut pos, builtin_index);
|
||||||
|
|
||||||
|
let global_index_arg = pos.ins().iconst(I32, index.as_u32() as i64);
|
||||||
|
pos.ins()
|
||||||
|
.call_indirect(builtin_sig, builtin_addr, &[vmctx, global_index_arg, value]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap> {
|
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap> {
|
||||||
@@ -1141,28 +1204,19 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
func: &mut ir::Function,
|
func: &mut ir::Function,
|
||||||
index: GlobalIndex,
|
index: GlobalIndex,
|
||||||
) -> WasmResult<GlobalVariable> {
|
) -> WasmResult<GlobalVariable> {
|
||||||
let pointer_type = self.pointer_type();
|
// Although `ExternRef`s live at the same memory location as any other
|
||||||
|
// type of global at the same index would, getting or setting them
|
||||||
let (ptr, offset) = {
|
// requires ref counting barriers. Therefore, we need to use
|
||||||
let vmctx = self.vmctx(func);
|
// `GlobalVariable::Custom`, as that is the only kind of
|
||||||
if let Some(def_index) = self.module.defined_global_index(index) {
|
// `GlobalVariable` for which `cranelift-wasm` supports custom access
|
||||||
let offset =
|
// translation.
|
||||||
i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
|
if self.module.globals[index].wasm_ty == WasmType::ExternRef {
|
||||||
(vmctx, offset)
|
return Ok(GlobalVariable::Custom);
|
||||||
} else {
|
|
||||||
let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
|
|
||||||
let global = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: vmctx,
|
|
||||||
offset: Offset32::new(i32::try_from(from_offset).unwrap()),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
(global, 0)
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
let (gv, offset) = self.get_global_location(func, index);
|
||||||
Ok(GlobalVariable::Memory {
|
Ok(GlobalVariable::Memory {
|
||||||
gv: ptr,
|
gv,
|
||||||
offset: offset.into(),
|
offset: offset.into(),
|
||||||
ty: self.module.globals[index].ty,
|
ty: self.module.globals[index].ty,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityR
|
|||||||
use wasmtime_environ::wasm::{
|
use wasmtime_environ::wasm::{
|
||||||
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
|
||||||
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableElementType,
|
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableElementType,
|
||||||
TableIndex,
|
TableIndex, WasmType,
|
||||||
};
|
};
|
||||||
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
|
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
|
||||||
|
|
||||||
@@ -226,6 +226,21 @@ impl Instance {
|
|||||||
unsafe { self.globals_ptr().add(index) }
|
unsafe { self.globals_ptr().add(index) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a raw pointer to the global at the given index regardless whether it
|
||||||
|
/// is defined locally or imported from another module.
|
||||||
|
///
|
||||||
|
/// Panics if the index is out of bound or is the reserved value.
|
||||||
|
pub(crate) fn defined_or_imported_global_ptr(
|
||||||
|
&self,
|
||||||
|
index: GlobalIndex,
|
||||||
|
) -> *mut VMGlobalDefinition {
|
||||||
|
if let Some(index) = self.module.local.defined_global_index(index) {
|
||||||
|
self.global_ptr(index)
|
||||||
|
} else {
|
||||||
|
self.imported_global(index).from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a pointer to the `VMGlobalDefinition`s.
|
/// Return a pointer to the `VMGlobalDefinition`s.
|
||||||
fn globals_ptr(&self) -> *mut VMGlobalDefinition {
|
fn globals_ptr(&self) -> *mut VMGlobalDefinition {
|
||||||
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) }
|
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) }
|
||||||
@@ -1390,8 +1405,16 @@ fn initialize_globals(instance: &Instance) {
|
|||||||
};
|
};
|
||||||
*to = from;
|
*to = from;
|
||||||
}
|
}
|
||||||
|
GlobalInit::RefFunc(f) => {
|
||||||
|
*(*to).as_anyfunc_mut() = instance.get_caller_checked_anyfunc(f).unwrap()
|
||||||
|
as *const VMCallerCheckedAnyfunc;
|
||||||
|
}
|
||||||
|
GlobalInit::RefNullConst => match global.wasm_ty {
|
||||||
|
WasmType::FuncRef => *(*to).as_anyfunc_mut() = ptr::null(),
|
||||||
|
WasmType::ExternRef => *(*to).as_externref_mut() = None,
|
||||||
|
ty => panic!("unsupported reference type for global: {:?}", ty),
|
||||||
|
},
|
||||||
GlobalInit::Import => panic!("locally-defined global initialized as import"),
|
GlobalInit::Import => panic!("locally-defined global initialized as import"),
|
||||||
GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,3 +67,14 @@ pub fn ref_type() -> wasmtime_environ::ir::Type {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Cranelift IR type used for pointer types for this target architecture.
|
||||||
|
pub fn pointer_type() -> wasmtime_environ::ir::Type {
|
||||||
|
if cfg!(target_pointer_width = "32") {
|
||||||
|
wasmtime_environ::ir::types::I32
|
||||||
|
} else if cfg!(target_pointer_width = "64") {
|
||||||
|
wasmtime_environ::ir::types::I64
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,9 +60,11 @@ use crate::externref::VMExternRef;
|
|||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::traphandlers::raise_lib_trap;
|
use crate::traphandlers::raise_lib_trap;
|
||||||
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
|
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
|
||||||
use std::ptr::NonNull;
|
use std::mem;
|
||||||
|
use std::ptr::{self, NonNull};
|
||||||
use wasmtime_environ::wasm::{
|
use wasmtime_environ::wasm::{
|
||||||
DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableElementType, TableIndex,
|
DataIndex, DefinedMemoryIndex, ElemIndex, GlobalIndex, MemoryIndex, TableElementType,
|
||||||
|
TableIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Implementation of f32.ceil
|
/// Implementation of f32.ceil
|
||||||
@@ -430,3 +432,47 @@ pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc(
|
|||||||
let registry = &**instance.stack_map_registry();
|
let registry = &**instance.stack_map_registry();
|
||||||
activations_table.insert_with_gc(externref, registry);
|
activations_table.insert_with_gc(externref, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a Wasm `global.get` for `externref` globals.
|
||||||
|
pub unsafe extern "C" fn wasmtime_externref_global_get(
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
index: u32,
|
||||||
|
) -> *mut u8 {
|
||||||
|
let index = GlobalIndex::from_u32(index);
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
let global = instance.defined_or_imported_global_ptr(index);
|
||||||
|
match (*global).as_externref().clone() {
|
||||||
|
None => ptr::null_mut(),
|
||||||
|
Some(externref) => {
|
||||||
|
let raw = externref.as_raw();
|
||||||
|
let activations_table = &**instance.externref_activations_table();
|
||||||
|
let registry = &**instance.stack_map_registry();
|
||||||
|
activations_table.insert_with_gc(externref, registry);
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a Wasm `global.set` for `externref` globals.
|
||||||
|
pub unsafe extern "C" fn wasmtime_externref_global_set(
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
index: u32,
|
||||||
|
externref: *mut u8,
|
||||||
|
) {
|
||||||
|
let externref = if externref.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(VMExternRef::clone_from_raw(externref))
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = GlobalIndex::from_u32(index);
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
let global = instance.defined_or_imported_global_ptr(index);
|
||||||
|
|
||||||
|
// Swap the new `externref` value into the global before we drop the old
|
||||||
|
// value. This protects against an `externref` with a `Drop` implementation
|
||||||
|
// that calls back into Wasm and touches this global again (we want to avoid
|
||||||
|
// it observing a halfway-deinitialized value).
|
||||||
|
let old = mem::replace((*global).as_externref_mut(), externref);
|
||||||
|
drop(old);
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,4 +65,18 @@ impl SignatureRegistry {
|
|||||||
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
|
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
|
||||||
self.index2wasm.get(&idx).cloned()
|
self.index2wasm.get(&idx).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Looks up both a shared Wasm function signature and its associated native
|
||||||
|
/// `ir::Signature` within this registry.
|
||||||
|
///
|
||||||
|
/// Note that for this operation to be semantically correct the `idx` must
|
||||||
|
/// have previously come from a call to `register` of this same object.
|
||||||
|
pub fn lookup_wasm_and_native_signatures(
|
||||||
|
&self,
|
||||||
|
idx: VMSharedSignatureIndex,
|
||||||
|
) -> Option<(WasmFuncType, ir::Signature)> {
|
||||||
|
let wasm = self.lookup_wasm(idx)?;
|
||||||
|
let native = self.lookup_native(idx)?;
|
||||||
|
Some((wasm, native))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! This file declares `VMContext` and several related structs which contain
|
//! This file declares `VMContext` and several related structs which contain
|
||||||
//! fields that compiled wasm code accesses directly.
|
//! fields that compiled wasm code accesses directly.
|
||||||
|
|
||||||
|
use crate::externref::VMExternRef;
|
||||||
use crate::instance::Instance;
|
use crate::instance::Instance;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
@@ -267,6 +268,7 @@ pub struct VMGlobalDefinition {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_vmglobal_definition {
|
mod test_vmglobal_definition {
|
||||||
use super::VMGlobalDefinition;
|
use super::VMGlobalDefinition;
|
||||||
|
use crate::externref::VMExternRef;
|
||||||
use more_asserts::assert_ge;
|
use more_asserts::assert_ge;
|
||||||
use std::mem::{align_of, size_of};
|
use std::mem::{align_of, size_of};
|
||||||
use wasmtime_environ::{Module, VMOffsets};
|
use wasmtime_environ::{Module, VMOffsets};
|
||||||
@@ -296,6 +298,11 @@ mod test_vmglobal_definition {
|
|||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
|
assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmglobal_can_contain_externref() {
|
||||||
|
assert!(size_of::<VMExternRef>() <= size_of::<VMGlobalDefinition>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMGlobalDefinition {
|
impl VMGlobalDefinition {
|
||||||
@@ -423,6 +430,30 @@ impl VMGlobalDefinition {
|
|||||||
pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] {
|
pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut [u8; 16])
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut [u8; 16])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an externref.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_externref(&self) -> &Option<VMExternRef> {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const Option<VMExternRef>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an externref.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_externref_mut(&mut self) -> &mut Option<VMExternRef> {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut Option<VMExternRef>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an anyfunc.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_anyfunc(&self) -> *const VMCallerCheckedAnyfunc {
|
||||||
|
*(self.storage.as_ref().as_ptr() as *const *const VMCallerCheckedAnyfunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an anyfunc.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_anyfunc_mut(&mut self) -> &mut *const VMCallerCheckedAnyfunc {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut *const VMCallerCheckedAnyfunc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An index into the shared signature registry, usable for checking signatures
|
/// An index into the shared signature registry, usable for checking signatures
|
||||||
@@ -559,6 +590,10 @@ impl VMBuiltinFunctionsArray {
|
|||||||
wasmtime_drop_externref as usize;
|
wasmtime_drop_externref as usize;
|
||||||
ptrs[BuiltinFunctionIndex::activations_table_insert_with_gc().index() as usize] =
|
ptrs[BuiltinFunctionIndex::activations_table_insert_with_gc().index() as usize] =
|
||||||
wasmtime_activations_table_insert_with_gc as usize;
|
wasmtime_activations_table_insert_with_gc as usize;
|
||||||
|
ptrs[BuiltinFunctionIndex::externref_global_get().index() as usize] =
|
||||||
|
wasmtime_externref_global_get as usize;
|
||||||
|
ptrs[BuiltinFunctionIndex::externref_global_set().index() as usize] =
|
||||||
|
wasmtime_externref_global_set as usize;
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
for i in 0..ptrs.len() {
|
for i in 0..ptrs.len() {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use crate::{
|
|||||||
ValType,
|
ValType,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use wasmtime_environ::wasm;
|
use wasmtime_environ::wasm;
|
||||||
use wasmtime_runtime::{self as runtime, InstanceHandle};
|
use wasmtime_runtime::{self as runtime, InstanceHandle};
|
||||||
@@ -227,6 +229,15 @@ impl Global {
|
|||||||
ValType::I64 => Val::from(*definition.as_i64()),
|
ValType::I64 => Val::from(*definition.as_i64()),
|
||||||
ValType::F32 => Val::F32(*definition.as_u32()),
|
ValType::F32 => Val::F32(*definition.as_u32()),
|
||||||
ValType::F64 => Val::F64(*definition.as_u64()),
|
ValType::F64 => Val::F64(*definition.as_u64()),
|
||||||
|
ValType::ExternRef => Val::ExternRef(
|
||||||
|
definition
|
||||||
|
.as_externref()
|
||||||
|
.clone()
|
||||||
|
.map(|inner| ExternRef { inner }),
|
||||||
|
),
|
||||||
|
ValType::FuncRef => {
|
||||||
|
from_checked_anyfunc(definition.as_anyfunc() as *mut _, &self.instance.store)
|
||||||
|
}
|
||||||
ty => unimplemented!("Global::get for {:?}", ty),
|
ty => unimplemented!("Global::get for {:?}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,6 +267,19 @@ impl Global {
|
|||||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||||
|
Val::FuncRef(f) => {
|
||||||
|
*definition.as_anyfunc_mut() = f.map_or(ptr::null(), |f| {
|
||||||
|
f.caller_checked_anyfunc().as_ptr() as *const _
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Val::ExternRef(x) => {
|
||||||
|
// In case the old value's `Drop` implementation is
|
||||||
|
// re-entrant and tries to touch this global again, do a
|
||||||
|
// replace, and then drop. This way no one can observe a
|
||||||
|
// halfway-deinitialized value.
|
||||||
|
let old = mem::replace(definition.as_externref_mut(), x.map(|x| x.inner));
|
||||||
|
drop(old);
|
||||||
|
}
|
||||||
_ => unimplemented!("Global::set for {:?}", val.ty()),
|
_ => unimplemented!("Global::set for {:?}", val.ty()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use std::panic::{self, AssertUnwindSafe};
|
|||||||
use std::ptr::{self, NonNull};
|
use std::ptr::{self, NonNull};
|
||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMTrampoline,
|
raise_user_trap, Export, InstanceHandle, VMContext, VMFunctionBody, VMSharedSignatureIndex,
|
||||||
|
VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A WebAssembly function which can be called.
|
/// A WebAssembly function which can be called.
|
||||||
@@ -494,19 +495,20 @@ impl Func {
|
|||||||
func.into_func(store)
|
func.into_func(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sig_index(&self) -> VMSharedSignatureIndex {
|
||||||
|
unsafe { self.export.anyfunc.as_ref().type_index }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the underlying wasm type that this `Func` has.
|
/// Returns the underlying wasm type that this `Func` has.
|
||||||
pub fn ty(&self) -> FuncType {
|
pub fn ty(&self) -> FuncType {
|
||||||
// Signatures should always be registered in the store's registry of
|
// Signatures should always be registered in the store's registry of
|
||||||
// shared signatures, so we should be able to unwrap safely here.
|
// shared signatures, so we should be able to unwrap safely here.
|
||||||
let sig = self
|
let wft = self.instance.store.lookup_signature(self.sig_index());
|
||||||
.instance
|
|
||||||
.store
|
|
||||||
.lookup_signature(unsafe { self.export.anyfunc.as_ref().type_index });
|
|
||||||
|
|
||||||
// This is only called with `Export::Function`, and since it's coming
|
// This is only called with `Export::Function`, and since it's coming
|
||||||
// from wasmtime_runtime itself we should support all the types coming
|
// from wasmtime_runtime itself we should support all the types coming
|
||||||
// out of it, so assert such here.
|
// out of it, so assert such here.
|
||||||
FuncType::from_wasm_func_type(&sig).expect("core wasm signature should be supported")
|
FuncType::from_wasm_func_type(&wft).expect("core wasm signature should be supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of parameters that this function takes.
|
/// Returns the number of parameters that this function takes.
|
||||||
|
|||||||
@@ -900,6 +900,17 @@ impl Store {
|
|||||||
.expect("failed to lookup signature")
|
.expect("failed to lookup signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup_wasm_and_native_signatures(
|
||||||
|
&self,
|
||||||
|
sig_index: VMSharedSignatureIndex,
|
||||||
|
) -> (wasm::WasmFuncType, ir::Signature) {
|
||||||
|
self.inner
|
||||||
|
.signatures
|
||||||
|
.borrow()
|
||||||
|
.lookup_wasm_and_native_signatures(sig_index)
|
||||||
|
.expect("failed to lookup signature")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn register_signature(
|
pub(crate) fn register_signature(
|
||||||
&self,
|
&self,
|
||||||
wasm_sig: wasm::WasmFuncType,
|
wasm_sig: wasm::WasmFuncType,
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use std::any::Any;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex};
|
||||||
use wasmtime_environ::Module;
|
use wasmtime_environ::Module;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
||||||
VMSharedSignatureIndex, VMTrampoline,
|
VMFunctionImport, VMSharedSignatureIndex, VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn create_handle(
|
pub(crate) fn create_handle(
|
||||||
@@ -20,9 +20,10 @@ pub(crate) fn create_handle(
|
|||||||
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||||
state: Box<dyn Any>,
|
state: Box<dyn Any>,
|
||||||
|
func_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
|
||||||
) -> Result<StoreInstanceHandle> {
|
) -> Result<StoreInstanceHandle> {
|
||||||
let imports = Imports::new(
|
let imports = Imports::new(
|
||||||
PrimaryMap::new(),
|
func_imports,
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
use super::create_handle::create_handle;
|
use super::create_handle::create_handle;
|
||||||
use crate::trampoline::StoreInstanceHandle;
|
use crate::trampoline::StoreInstanceHandle;
|
||||||
use crate::{FuncType, Store, Trap};
|
use crate::{FuncType, Store, Trap};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -211,10 +211,7 @@ pub fn create_handle_with_function(
|
|||||||
let isa = store.engine().config().target_isa();
|
let isa = store.engine().config().target_isa();
|
||||||
|
|
||||||
let pointer_type = isa.pointer_type();
|
let pointer_type = isa.pointer_type();
|
||||||
let sig = match ft.get_wasmtime_signature(pointer_type) {
|
let sig = ft.get_wasmtime_signature(pointer_type);
|
||||||
Some(sig) => sig,
|
|
||||||
None => bail!("not a supported core wasm signature {:?}", ft),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
@@ -259,6 +256,7 @@ pub fn create_handle_with_function(
|
|||||||
finished_functions,
|
finished_functions,
|
||||||
trampolines,
|
trampolines,
|
||||||
Box::new(trampoline_state),
|
Box::new(trampoline_state),
|
||||||
|
PrimaryMap::new(),
|
||||||
)
|
)
|
||||||
.map(|instance| (instance, trampoline))
|
.map(|instance| (instance, trampoline))
|
||||||
}
|
}
|
||||||
@@ -277,10 +275,7 @@ pub unsafe fn create_handle_with_raw_function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let pointer_type = isa.pointer_type();
|
let pointer_type = isa.pointer_type();
|
||||||
let sig = match ft.get_wasmtime_signature(pointer_type) {
|
let sig = ft.get_wasmtime_signature(pointer_type);
|
||||||
Some(sig) => sig,
|
|
||||||
None => bail!("not a supported core wasm signature {:?}", ft),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
let mut finished_functions = PrimaryMap::new();
|
let mut finished_functions = PrimaryMap::new();
|
||||||
@@ -298,5 +293,12 @@ pub unsafe fn create_handle_with_raw_function(
|
|||||||
let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
|
let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
|
||||||
trampolines.insert(sig_id, trampoline);
|
trampolines.insert(sig_id, trampoline);
|
||||||
|
|
||||||
create_handle(module, store, finished_functions, trampolines, state)
|
create_handle(
|
||||||
|
module,
|
||||||
|
store,
|
||||||
|
finished_functions,
|
||||||
|
trampolines,
|
||||||
|
state,
|
||||||
|
PrimaryMap::new(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
use super::create_handle::create_handle;
|
use super::create_handle::create_handle;
|
||||||
use crate::trampoline::StoreInstanceHandle;
|
use crate::trampoline::StoreInstanceHandle;
|
||||||
use crate::{GlobalType, Mutability, Store, Val};
|
use crate::{GlobalType, Mutability, Store, Val};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::{wasm, EntityIndex, Module};
|
use wasmtime_environ::{wasm, EntityIndex, Module};
|
||||||
|
use wasmtime_runtime::VMFunctionImport;
|
||||||
|
|
||||||
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
|
||||||
|
let mut module = Module::new();
|
||||||
|
let mut func_imports = PrimaryMap::new();
|
||||||
|
let mut externref_init = None;
|
||||||
|
|
||||||
let global = wasm::Global {
|
let global = wasm::Global {
|
||||||
wasm_ty: gt.content().to_wasm_type(),
|
wasm_ty: gt.content().to_wasm_type(),
|
||||||
ty: match gt.content().get_wasmtime_type() {
|
ty: gt.content().get_wasmtime_type(),
|
||||||
Some(t) => t,
|
|
||||||
None => bail!("cannot support {:?} as a wasm global type", gt.content()),
|
|
||||||
},
|
|
||||||
mutability: match gt.mutability() {
|
mutability: match gt.mutability() {
|
||||||
Mutability::Const => false,
|
Mutability::Const => false,
|
||||||
Mutability::Var => true,
|
Mutability::Var => true,
|
||||||
@@ -21,10 +23,42 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
|
|||||||
Val::I64(i) => wasm::GlobalInit::I64Const(i),
|
Val::I64(i) => wasm::GlobalInit::I64Const(i),
|
||||||
Val::F32(f) => wasm::GlobalInit::F32Const(f),
|
Val::F32(f) => wasm::GlobalInit::F32Const(f),
|
||||||
Val::F64(f) => wasm::GlobalInit::F64Const(f),
|
Val::F64(f) => wasm::GlobalInit::F64Const(f),
|
||||||
|
Val::ExternRef(None) | Val::FuncRef(None) => wasm::GlobalInit::RefNullConst,
|
||||||
|
Val::ExternRef(Some(x)) => {
|
||||||
|
// There is no `GlobalInit` variant for using an existing
|
||||||
|
// `externref` that isn't an import (because Wasm can't create
|
||||||
|
// an `externref` by itself). Therefore, initialize the global
|
||||||
|
// as null, and then monkey patch it after instantiation below.
|
||||||
|
externref_init = Some(x);
|
||||||
|
wasm::GlobalInit::RefNullConst
|
||||||
|
}
|
||||||
|
Val::FuncRef(Some(f)) => {
|
||||||
|
// Add a function import to the stub module, and then initialize
|
||||||
|
// our global with a `ref.func` to grab that imported function.
|
||||||
|
let shared_sig_index = f.sig_index();
|
||||||
|
let local_sig_index = module
|
||||||
|
.local
|
||||||
|
.signatures
|
||||||
|
.push(store.lookup_wasm_and_native_signatures(shared_sig_index));
|
||||||
|
let func_index = module.local.functions.push(local_sig_index);
|
||||||
|
module.local.num_imported_funcs = 1;
|
||||||
|
module
|
||||||
|
.imports
|
||||||
|
.push(("".into(), "".into(), EntityIndex::Function(func_index)));
|
||||||
|
|
||||||
|
let f = f.caller_checked_anyfunc();
|
||||||
|
let f = unsafe { f.as_ref() };
|
||||||
|
func_imports.push(VMFunctionImport {
|
||||||
|
body: f.func_ptr,
|
||||||
|
vmctx: f.vmctx,
|
||||||
|
});
|
||||||
|
|
||||||
|
wasm::GlobalInit::RefFunc(func_index)
|
||||||
|
}
|
||||||
_ => unimplemented!("create_global for {:?}", gt),
|
_ => unimplemented!("create_global for {:?}", gt),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut module = Module::new();
|
|
||||||
let global_id = module.local.globals.push(global);
|
let global_id = module.local.globals.push(global);
|
||||||
module
|
module
|
||||||
.exports
|
.exports
|
||||||
@@ -35,6 +69,17 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
|
func_imports,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if let Some(x) = externref_init {
|
||||||
|
match handle.lookup("global").unwrap() {
|
||||||
|
wasmtime_runtime::Export::Global(g) => unsafe {
|
||||||
|
*(*g.definition).as_externref_mut() = Some(x.inner);
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(handle)
|
Ok(handle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub fn create_handle_with_memory(
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
|
PrimaryMap::new(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
|
|||||||
PrimaryMap::new(),
|
PrimaryMap::new(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Box::new(()),
|
Box::new(()),
|
||||||
|
PrimaryMap::new(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,15 +99,15 @@ impl ValType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_wasmtime_type(&self) -> Option<ir::Type> {
|
pub(crate) fn get_wasmtime_type(&self) -> ir::Type {
|
||||||
match self {
|
match self {
|
||||||
ValType::I32 => Some(ir::types::I32),
|
ValType::I32 => ir::types::I32,
|
||||||
ValType::I64 => Some(ir::types::I64),
|
ValType::I64 => ir::types::I64,
|
||||||
ValType::F32 => Some(ir::types::F32),
|
ValType::F32 => ir::types::F32,
|
||||||
ValType::F64 => Some(ir::types::F64),
|
ValType::F64 => ir::types::F64,
|
||||||
ValType::V128 => Some(ir::types::I8X16),
|
ValType::V128 => ir::types::I8X16,
|
||||||
ValType::ExternRef => Some(wasmtime_runtime::ref_type()),
|
ValType::ExternRef => wasmtime_runtime::ref_type(),
|
||||||
_ => None,
|
ValType::FuncRef => wasmtime_runtime::pointer_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,34 +248,32 @@ impl FuncType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `Some` if this function signature was compatible with cranelift,
|
/// Get the Cranelift-compatible function signature.
|
||||||
/// or `None` if one of the types/results wasn't supported or compatible
|
pub(crate) fn get_wasmtime_signature(&self, pointer_type: ir::Type) -> ir::Signature {
|
||||||
/// with cranelift.
|
|
||||||
pub(crate) fn get_wasmtime_signature(&self, pointer_type: ir::Type) -> Option<ir::Signature> {
|
|
||||||
use wasmtime_environ::ir::{AbiParam, ArgumentPurpose, Signature};
|
use wasmtime_environ::ir::{AbiParam, ArgumentPurpose, Signature};
|
||||||
use wasmtime_jit::native;
|
use wasmtime_jit::native;
|
||||||
let call_conv = native::call_conv();
|
let call_conv = native::call_conv();
|
||||||
let mut params = self
|
let mut params = self
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
|
.map(|p| AbiParam::new(p.get_wasmtime_type()))
|
||||||
.collect::<Option<Vec<_>>>()?;
|
.collect::<Vec<_>>();
|
||||||
let returns = self
|
let returns = self
|
||||||
.results
|
.results
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
|
.map(|p| AbiParam::new(p.get_wasmtime_type()))
|
||||||
.collect::<Option<Vec<_>>>()?;
|
.collect::<Vec<_>>();
|
||||||
params.insert(
|
params.insert(
|
||||||
0,
|
0,
|
||||||
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
|
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
|
||||||
);
|
);
|
||||||
params.insert(1, AbiParam::new(pointer_type));
|
params.insert(1, AbiParam::new(pointer_type));
|
||||||
|
|
||||||
Some(Signature {
|
Signature {
|
||||||
params,
|
params,
|
||||||
returns,
|
returns,
|
||||||
call_conv,
|
call_conv,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `None` if any types in the signature can't be converted to the
|
/// Returns `None` if any types in the signature can't be converted to the
|
||||||
|
|||||||
@@ -111,3 +111,73 @@ fn cross_store() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_set_externref_globals_via_api() -> anyhow::Result<()> {
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.wasm_reference_types(true);
|
||||||
|
let engine = Engine::new(&cfg);
|
||||||
|
let store = Store::new(&engine);
|
||||||
|
|
||||||
|
// Initialize with a null externref.
|
||||||
|
|
||||||
|
let global = Global::new(
|
||||||
|
&store,
|
||||||
|
GlobalType::new(ValType::ExternRef, Mutability::Var),
|
||||||
|
Val::ExternRef(None),
|
||||||
|
)?;
|
||||||
|
assert!(global.get().unwrap_externref().is_none());
|
||||||
|
|
||||||
|
global.set(Val::ExternRef(Some(ExternRef::new("hello".to_string()))))?;
|
||||||
|
let r = global.get().unwrap_externref().unwrap();
|
||||||
|
assert!(r.data().is::<String>());
|
||||||
|
assert_eq!(r.data().downcast_ref::<String>().unwrap(), "hello");
|
||||||
|
|
||||||
|
// Initialize with a non-null externref.
|
||||||
|
|
||||||
|
let global = Global::new(
|
||||||
|
&store,
|
||||||
|
GlobalType::new(ValType::ExternRef, Mutability::Const),
|
||||||
|
Val::ExternRef(Some(ExternRef::new(42_i32))),
|
||||||
|
)?;
|
||||||
|
let r = global.get().unwrap_externref().unwrap();
|
||||||
|
assert!(r.data().is::<i32>());
|
||||||
|
assert_eq!(r.data().downcast_ref::<i32>().copied().unwrap(), 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_set_funcref_globals_via_api() -> anyhow::Result<()> {
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.wasm_reference_types(true);
|
||||||
|
let engine = Engine::new(&cfg);
|
||||||
|
let store = Store::new(&engine);
|
||||||
|
|
||||||
|
let f = Func::wrap(&store, || {});
|
||||||
|
|
||||||
|
// Initialize with a null funcref.
|
||||||
|
|
||||||
|
let global = Global::new(
|
||||||
|
&store,
|
||||||
|
GlobalType::new(ValType::FuncRef, Mutability::Var),
|
||||||
|
Val::FuncRef(None),
|
||||||
|
)?;
|
||||||
|
assert!(global.get().unwrap_funcref().is_none());
|
||||||
|
|
||||||
|
global.set(Val::FuncRef(Some(f.clone())))?;
|
||||||
|
let f2 = global.get().unwrap_funcref().cloned().unwrap();
|
||||||
|
assert_eq!(f.ty(), f2.ty());
|
||||||
|
|
||||||
|
// Initialize with a non-null funcref.
|
||||||
|
|
||||||
|
let global = Global::new(
|
||||||
|
&store,
|
||||||
|
GlobalType::new(ValType::FuncRef, Mutability::Var),
|
||||||
|
Val::FuncRef(Some(f.clone())),
|
||||||
|
)?;
|
||||||
|
let f2 = global.get().unwrap_funcref().cloned().unwrap();
|
||||||
|
assert_eq!(f.ty(), f2.ty());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -517,3 +517,97 @@ fn pass_externref_into_wasm_during_destructor_in_gc() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gc_on_drop_in_mutable_externref_global() -> anyhow::Result<()> {
|
||||||
|
let (store, module) = ref_types_module(
|
||||||
|
r#"
|
||||||
|
(module
|
||||||
|
(global $g (mut externref) (ref.null extern))
|
||||||
|
|
||||||
|
(func (export "set-g") (param externref)
|
||||||
|
(global.set $g (local.get 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let instance = Instance::new(&store, &module, &[])?;
|
||||||
|
let set_g = instance.get_func("set-g").unwrap();
|
||||||
|
|
||||||
|
let gc_count = Rc::new(Cell::new(0));
|
||||||
|
|
||||||
|
// Put a `GcOnDrop` into the global.
|
||||||
|
{
|
||||||
|
let args = vec![Val::ExternRef(Some(ExternRef::new(GcOnDrop {
|
||||||
|
store: store.clone(),
|
||||||
|
gc_count: gc_count.clone(),
|
||||||
|
})))];
|
||||||
|
set_g.call(&args)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the `GcOnDrop` from the `VMExternRefActivationsTable`.
|
||||||
|
store.gc();
|
||||||
|
|
||||||
|
// Overwrite the `GcOnDrop` global value, causing it to be dropped, and
|
||||||
|
// triggering a GC.
|
||||||
|
assert_eq!(gc_count.get(), 0);
|
||||||
|
set_g.call(&[Val::ExternRef(None)])?;
|
||||||
|
assert_eq!(gc_count.get(), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn touch_own_externref_global_on_drop() -> anyhow::Result<()> {
|
||||||
|
let (store, module) = ref_types_module(
|
||||||
|
r#"
|
||||||
|
(module
|
||||||
|
(global $g (export "g") (mut externref) (ref.null extern))
|
||||||
|
|
||||||
|
(func (export "set-g") (param externref)
|
||||||
|
(global.set $g (local.get 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let instance = Instance::new(&store, &module, &[])?;
|
||||||
|
let g = instance.get_global("g").unwrap();
|
||||||
|
let set_g = instance.get_func("set-g").unwrap();
|
||||||
|
|
||||||
|
let touched = Rc::new(Cell::new(false));
|
||||||
|
|
||||||
|
{
|
||||||
|
let args = vec![Val::ExternRef(Some(ExternRef::new(TouchGlobalOnDrop {
|
||||||
|
g,
|
||||||
|
touched: touched.clone(),
|
||||||
|
})))];
|
||||||
|
set_g.call(&args)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the `TouchGlobalOnDrop` from the `VMExternRefActivationsTable`.
|
||||||
|
store.gc();
|
||||||
|
|
||||||
|
assert!(!touched.get());
|
||||||
|
set_g.call(&[Val::ExternRef(Some(ExternRef::new("hello".to_string())))])?;
|
||||||
|
assert!(touched.get());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
|
||||||
|
struct TouchGlobalOnDrop {
|
||||||
|
g: Global,
|
||||||
|
touched: Rc<Cell<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TouchGlobalOnDrop {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// From the `Drop` implementation, we see the new global value, not
|
||||||
|
// `self`.
|
||||||
|
let r = self.g.get().unwrap_externref().unwrap();
|
||||||
|
assert!(r.data().is::<String>());
|
||||||
|
assert_eq!(r.data().downcast_ref::<String>().unwrap(), "hello");
|
||||||
|
self.touched.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
;; This test contains the changes in
|
||||||
|
;; https://github.com/WebAssembly/reference-types/pull/104, and can be deleted
|
||||||
|
;; once that merges and we update our upstream tests.
|
||||||
|
|
||||||
|
(module
|
||||||
|
(global $mr (mut externref) (ref.null extern))
|
||||||
|
(func (export "get-mr") (result externref) (global.get $mr))
|
||||||
|
(func (export "set-mr") (param externref) (global.set $mr (local.get 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "get-mr") (ref.null extern))
|
||||||
|
(assert_return (invoke "set-mr" (ref.extern 10)))
|
||||||
|
(assert_return (invoke "get-mr") (ref.extern 10))
|
||||||
Reference in New Issue
Block a user