diff --git a/lib/api/src/extern_ref.rs b/lib/api/src/extern_ref.rs new file mode 100644 index 000000000..6371efd74 --- /dev/null +++ b/lib/api/src/extern_ref.rs @@ -0,0 +1,66 @@ +use crate::FromToNativeWasmType; +use std::any::Any; +use std::marker::PhantomData; +use wasmer_vm::VMExternRef; + +#[derive(Debug)] +#[repr(transparent)] +/// An opaque reference to some data. This reference can be passed through Wasm. +pub struct ExternRef { + inner: VMExternRef, + _phantom: PhantomData, +} + +impl ExternRef +where + T: Any + Send + Sync + 'static + Sized, +{ + /// Checks if the given ExternRef is null. + pub fn is_null(&self) -> bool { + self.inner.is_null() + } + + /// New null extern ref + pub fn null() -> Self { + Self { + inner: VMExternRef::null(), + _phantom: PhantomData, + } + } + + /// Make a new extern reference + pub fn new(value: T) -> Self { + Self { + inner: VMExternRef::new(value), + _phantom: PhantomData, + } + } + + /// Try to downcast to the given value + pub fn downcast(&self) -> Option<&T> { + self.inner.downcast::() + } +} + +/* +unsafe impl FromToNativeWasmType for ExternRef +where + T: Any + Send + Sync + 'static + Sized, +{ + type Native = usize; + + #[inline] + fn from_native(native: Self::Native) -> Self { + let inner = VMExternRef::from_ne_bytes(Self::Native::to_ne_bytes(native)); + Self { + inner, + _phantom: PhantomData, + } + } + + #[inline] + fn to_native(self) -> Self::Native { + Self::Native::from_ne_bytes(VMExternRef::to_ne_bytes(self.inner)) + } +} +*/ diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index ec8b8b1ec..1e657becc 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -255,6 +255,7 @@ mod env; mod exports; +mod extern_ref; mod externals; mod import_object; mod instance; @@ -285,6 +286,7 @@ pub mod internals { pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv}; pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator}; +pub use crate::extern_ref::ExternRef; pub use crate::externals::{ Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList, }; diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs new file mode 100644 index 000000000..43afea1f5 --- /dev/null +++ b/lib/api/tests/reference_types.rs @@ -0,0 +1,83 @@ +use anyhow::Result; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use wasmer::*; + +#[test] +fn func_ref_passed_and_returned() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (import "env" "func_ref_identity" (func (param funcref) (result funcref))) + (type $ret_i32_ty (func (result i32))) + (table $table (export "table") 2 2 funcref) + + (func (export "run") (param) (result funcref) + (call 0 (ref.null func))) + (func (export "call_set_value") (param $fr funcref) (result i32) + (table.set $table (i32.const 0) (local.get $fr)) + (call_indirect $table (type $ret_i32_ty) (i32.const 0))) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + "env" => { + "func_ref_identity" => Function::new(&store, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |values| -> Result, _> { + Ok(vec![values[0].clone()]) + }) + }, + }; + + let instance = Instance::new(&module, &imports)?; + + let f: &Function = instance.exports.get_function("run")?; + let results = f.call(&[]).unwrap(); + if let Value::FuncRef(fr) = &results[0] { + assert!(fr.is_none()); + } else { + panic!("funcref not found!"); + } + + #[derive(Clone, Debug, WasmerEnv)] + pub struct Env(Arc); + let env = Env(Arc::new(AtomicBool::new(false))); + + let func_to_call = Function::new_native_with_env(&store, env.clone(), |env: &Env| -> i32 { + env.0.store(true, Ordering::SeqCst); + 343 + }); + let call_set_value: &Function = instance.exports.get_function("call_set_value")?; + let results: Box<[Value]> = call_set_value.call(&[Value::FuncRef(Some(func_to_call))])?; + assert!(env.0.load(Ordering::SeqCst)); + assert_eq!(&*results, &[Value::I32(343)]); + + Ok(()) +} + +#[test] +fn extern_ref_passed_and_returned() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (import "env" "extern_ref_identity" (func (param externref) (result externref))) + + (func (export "run") (param) (result externref) + (call 0 (ref.null extern))) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + "env" => { + "extern_ref_identity" => Function::new(&store, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |values| -> Result, _> { + Ok(vec![values[0].clone()]) + }) + }, + }; + + let instance = Instance::new(&module, &imports)?; + let f: &Function = instance.exports.get_function("run")?; + let results = f.call(&[]).unwrap(); + if let Value::ExternRef(er) = results[0] { + assert!(er.is_null()); + } else { + panic!("result is not an extern ref!"); + } + + Ok(()) +} diff --git a/lib/wasmer-types/src/extern_ref.rs b/lib/wasmer-types/src/extern_ref.rs index 3bde28138..cc04f3fac 100644 --- a/lib/wasmer-types/src/extern_ref.rs +++ b/lib/wasmer-types/src/extern_ref.rs @@ -30,6 +30,32 @@ impl VMExternRef { Self(Box::into_raw(Box::new(VMExternRefInner::new::(value)))) } + /// Try to downcast to the given value + pub fn downcast(&self) -> Option<&T> + where + T: Any + Send + Sync + 'static + Sized, + { + if self.is_null() { + return None; + } + unsafe { + let inner = &*self.0; + + inner.data.downcast_ref::() + } + } + + /// Return the memory representation of this extern ref as a byte array in + /// native byte order. + pub fn to_ne_bytes(&self) -> [u8; 8] { + usize::to_ne_bytes(self.0 as usize) + } + /// Convert native endian bytes to an extern ref. + // TODO: this should be unsafe!! + pub fn from_ne_bytes(bytes: [u8; 8]) -> Self { + Self(usize::from_ne_bytes(bytes) as *const _) + } + /// A deep copy of the reference, increments the strong count. pub fn ref_clone(&self) -> Self { if self.0.is_null() {