mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-12 21:38:39 +00:00
Add basic API tests with reference types with dynamic functions
This commit is contained in:
66
lib/api/src/extern_ref.rs
Normal file
66
lib/api/src/extern_ref.rs
Normal file
@@ -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<T: Any + Send + Sync + 'static + Sized> {
|
||||||
|
inner: VMExternRef,
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ExternRef<T>
|
||||||
|
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::<T>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
unsafe impl<T> FromToNativeWasmType for ExternRef<T>
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -255,6 +255,7 @@
|
|||||||
|
|
||||||
mod env;
|
mod env;
|
||||||
mod exports;
|
mod exports;
|
||||||
|
mod extern_ref;
|
||||||
mod externals;
|
mod externals;
|
||||||
mod import_object;
|
mod import_object;
|
||||||
mod instance;
|
mod instance;
|
||||||
@@ -285,6 +286,7 @@ pub mod internals {
|
|||||||
|
|
||||||
pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv};
|
pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv};
|
||||||
pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator};
|
pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator};
|
||||||
|
pub use crate::extern_ref::ExternRef;
|
||||||
pub use crate::externals::{
|
pub use crate::externals::{
|
||||||
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
|
Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList,
|
||||||
};
|
};
|
||||||
|
|||||||
83
lib/api/tests/reference_types.rs
Normal file
83
lib/api/tests/reference_types.rs
Normal file
@@ -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<Vec<_>, _> {
|
||||||
|
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<AtomicBool>);
|
||||||
|
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<Vec<_>, _> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
@@ -30,6 +30,32 @@ impl VMExternRef {
|
|||||||
Self(Box::into_raw(Box::new(VMExternRefInner::new::<T>(value))))
|
Self(Box::into_raw(Box::new(VMExternRefInner::new::<T>(value))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to downcast to the given value
|
||||||
|
pub fn downcast<T>(&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::<T>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
/// A deep copy of the reference, increments the strong count.
|
||||||
pub fn ref_clone(&self) -> Self {
|
pub fn ref_clone(&self) -> Self {
|
||||||
if self.0.is_null() {
|
if self.0.is_null() {
|
||||||
|
|||||||
Reference in New Issue
Block a user