Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ExternRef in wasmer-js #3224

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/api/src/js/extern_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::any::Any;
use super::store::{AsStoreMut, AsStoreRef};
use crate::js::store::StoreId;

/// JS ExternRef, internally casted as a `Box<dyn Any>`
#[derive(Debug)]
pub struct ExternRef {
inner: Box<dyn Any>,
store_id: StoreId,
}

impl ExternRef {
/// Make a new extern reference
pub fn new<T: 'static>(store: &mut impl AsStoreMut, value: T) -> Self {
Self {
inner: Box::new(value),
store_id: store.as_store_ref().objects().id(),
}
}

/// Checks whether this object came from the store
pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.store_id == store.as_store_ref().objects().id()
}

/// Try to downcast to the given value.
pub fn downcast<'a, T>(&'a self, _store: &impl AsStoreRef) -> Option<&'a T>
where T: Any + Sized + 'static
{
self.inner.downcast_ref::<T>()
}
}
5 changes: 5 additions & 0 deletions lib/api/src/js/externals/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl Global {
val: Value,
mutability: Mutability,
) -> Result<Self, RuntimeError> {
use std::sync::Arc;
if !val.is_from_store(store) {
return Err(RuntimeError::new(
"cross-`WasmerEnv` values are not supported",
Expand All @@ -81,6 +82,10 @@ impl Global {
Value::I64(i) => ("i64", JsValue::from_f64(i as _)),
Value::F32(f) => ("f32", JsValue::from_f64(f as _)),
Value::F64(f) => ("f64", JsValue::from_f64(f)),
Value::ExternRef(Some(ref e)) => ("externref", JsValue::from_f64({
Arc::as_ptr(e) as usize as f64
})),
Value::ExternRef(None) => ("externref", JsValue::from_f64(0.0)),
_ => 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"
Expand Down
2 changes: 2 additions & 0 deletions lib/api/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub(crate) mod error;
mod export;
mod exports;
mod externals;
mod extern_ref;
mod function_env;
mod imports;
mod instance;
Expand Down Expand Up @@ -70,6 +71,7 @@ pub use crate::js::types::{
};
pub use crate::js::value::Value;
pub use crate::js::value::Value as Val;
pub use crate::js::extern_ref::ExternRef;

pub mod vm {
//! The `vm` module re-exports wasmer-vm types.
Expand Down
5 changes: 5 additions & 0 deletions lib/api/src/js/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use crate::js::store::AsStoreRef;
use crate::js::value::Value;
use wasm_bindgen::JsValue;
use std::sync::Arc;
pub use wasmer_types::{
ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability,
TableType, Type as ValType,
Expand Down Expand Up @@ -44,6 +45,10 @@ impl AsJs for Value {
Self::F32(f) => JsValue::from_f64(*f as f64),
Self::F64(f) => JsValue::from_f64(*f),
Self::V128(f) => JsValue::from_f64(*f as f64),
Self::ExternRef(None) => JsValue::null(),
Self::ExternRef(Some(f)) => JsValue::from_f64({
Arc::as_ptr(f) as usize as f64
}),
Self::FuncRef(Some(func)) => func
.handle
.get(store.as_store_ref().objects())
Expand Down
92 changes: 53 additions & 39 deletions lib/api/src/js/value.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::convert::TryFrom;
use std::fmt;
use std::string::{String, ToString};
use std::sync::Arc;

use wasmer_types::Type;

//use crate::ExternRef;
use crate::ExternRef;
use crate::js::externals::function::Function;

use super::store::{AsStoreMut, AsStoreRef};
Expand All @@ -14,7 +15,7 @@ use super::store::{AsStoreMut, AsStoreRef};
/// * Floating-point (32 or 64 bit width)
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
#[derive(Clone, PartialEq)]
#[derive(Clone)]
pub enum Value {
/// A 32-bit integer.
///
Expand All @@ -33,7 +34,7 @@ pub enum Value {
F64(f64),

/// An `externref` value which can hold opaque data to the wasm instance itself.
//ExternRef(Option<ExternRef>),
ExternRef(Option<Arc<ExternRef>>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why an Arc here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that the ExternRef should be the one storing the Arc (if really needed) (and hidden from the user)


/// A first-class reference to a WebAssembly function.
FuncRef(Option<Function>),
Expand All @@ -42,6 +43,23 @@ pub enum Value {
V128(u128),
}

impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use self::Value::*;
match (self, other) {
(I32(a), I32(b)) => a.eq(b),
(I64(a), I64(b)) => a.eq(b),
(F32(a), F32(b)) => a.eq(b),
(F64(a), F64(b)) => a.eq(b),
(ExternRef(Some(a)), ExternRef(Some(b))) => Arc::ptr_eq(a, b),
(ExternRef(None), ExternRef(None)) => true,
(FuncRef(a), FuncRef(b)) => a.eq(b),
(V128(a), V128(b)) => a.eq(b),
_ => false
}
}
}

macro_rules! accessors {
($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
/// Attempt to access the underlying value of this `Value`, returning
Expand Down Expand Up @@ -80,7 +98,7 @@ impl Value {
Self::F32(_) => Type::F32,
Self::F64(_) => Type::F64,
Self::V128(_) => Type::V128,
//Self::ExternRef(_) => Type::ExternRef,
Self::ExternRef(_) => Type::ExternRef,
Self::FuncRef(_) => Type::FuncRef,
}
}
Expand All @@ -101,28 +119,24 @@ impl Value {
.unwrap_or(0_f64), //TODO is this correct?

Self::FuncRef(None) => 0_f64,
//Self::ExternRef(Some(ref e)) => unsafe { *e.address().0 } as .into_raw(),
//Self::ExternRef(None) => externref: 0 },
Self::ExternRef(Some(ref e)) => Arc::as_ptr(e) as usize as f64,
Self::ExternRef(None) => 0_f64,
}
}

/// Converts a `f64` to a `Value`.
///
/// # Safety
///
pub unsafe fn from_raw(_store: &impl AsStoreRef, ty: Type, raw: f64) -> Self {
pub unsafe fn from_raw(store: &impl AsStoreRef, ty: Type, raw: f64) -> Self {
match ty {
Type::I32 => Self::I32(raw as _),
Type::I64 => Self::I64(raw as _),
Type::F32 => Self::F32(raw as _),
Type::F64 => Self::F64(raw),
Type::V128 => Self::V128(raw as _),
Type::FuncRef => todo!(),
Type::ExternRef => todo!(),
//Self::ExternRef(
//{
//VMExternRef::from_raw(raw).map(|e| ExternRef::from_vm_externref(store, e)),
//),
Type::ExternRef => Self::ExternRef(Some(unsafe { Arc::from_raw(raw as usize as *const _) })),
}
}

Expand All @@ -140,9 +154,9 @@ impl Value {
| Self::F32(_)
| Self::F64(_)
| Self::V128(_)
//| Self::ExternRef(None)
| Self::ExternRef(None)
| Self::FuncRef(None) => true,
//Self::ExternRef(Some(e)) => e.is_from_store(store),
Self::ExternRef(Some(e)) => (&*e).is_from_store(store),
Self::FuncRef(Some(f)) => f.is_from_store(store),
}
}
Expand All @@ -154,7 +168,7 @@ impl Value {
(F32(f32) f32 unwrap_f32 *e)
(F64(f64) f64 unwrap_f64 *e)
(V128(u128) v128 unwrap_v128 *e)
//(ExternRef(&Option<ExternRef>) externref unwrap_externref e)
(ExternRef(&Option<Arc<ExternRef>>) externref unwrap_externref e)
(FuncRef(&Option<Function>) funcref unwrap_funcref e)
}
}
Expand All @@ -167,8 +181,8 @@ impl fmt::Debug for Value {
Self::F32(v) => write!(f, "F32({:?})", v),
Self::F64(v) => write!(f, "F64({:?})", v),
Self::V128(v) => write!(f, "V128({:?})", v),
//Self::ExternRef(None) => write!(f, "Null ExternRef"),
//Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v),
Self::ExternRef(None) => write!(f, "Null ExternRef"),
Self::ExternRef(Some(v)) => write!(f, "ExternRef({:?})", v),
Self::FuncRef(None) => write!(f, "Null FuncRef"),
Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v),
}
Expand All @@ -183,7 +197,7 @@ impl ToString for Value {
Self::F32(v) => v.to_string(),
Self::F64(v) => v.to_string(),
Self::V128(v) => v.to_string(),
//Self::ExternRef(_) => "externref".to_string(),
Self::ExternRef(_) => "externref".to_string(),
Self::FuncRef(_) => "funcref".to_string(),
}
}
Expand Down Expand Up @@ -245,17 +259,17 @@ impl From<Option<Function>> for Value {
}
}

//impl From<ExternRef> for Value {
// fn from(val: ExternRef) -> Self {
// Self::ExternRef(Some(val))
// }
//}
//
//impl From<Option<ExternRef>> for Value {
// fn from(val: Option<ExternRef>) -> Self {
// Self::ExternRef(val)
// }
//}
impl From<Arc<ExternRef>> for Value {
fn from(val: Arc<ExternRef>) -> Self {
Self::ExternRef(Some(val))
}
}

impl From<Option<Arc<ExternRef>>> for Value {
fn from(val: Option<Arc<ExternRef>>) -> Self {
Self::ExternRef(val)
}
}

const NOT_I32: &str = "Value is not of Wasm type i32";
const NOT_I64: &str = "Value is not of Wasm type i64";
Expand Down Expand Up @@ -332,16 +346,16 @@ impl TryFrom<Value> for Option<Function> {
}
}

//impl TryFrom<Value> for Option<ExternRef> {
// type Error = &'static str;
//
// fn try_from(value: Value) -> Result<Self, Self::Error> {
// match value {
// Value::ExternRef(e) => Ok(e),
// _ => Err(NOT_EXTERNREF),
// }
// }
//}
impl TryFrom<Value> for Option<Arc<ExternRef>> {
type Error = &'static str;

fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::ExternRef(e) => Ok(e),
_ => Err("not an externref value"),
}
}
}

#[cfg(tests)]
mod tests {
Expand Down
32 changes: 32 additions & 0 deletions lib/api/tests/externals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,38 @@ fn function_new_env() -> Result<(), String> {
Ok(())
}

#[universal_test]
fn get_set_externref_globals_via_api() -> anyhow::Result<()> {
use crate::ExternRef;
use std::sync::Arc;

let mut store = Store::default();

// Initialize with a null externref.
let global = Global::new(
&mut store,
Value::ExternRef(None),
);
assert!(global.get(&mut store).unwrap_externref().is_none());

let value = Value::ExternRef(Some(Arc::new(ExternRef::new(&mut store, "hello".to_string()))));
global.set(&mut store, value)?;

let r = global.get(&mut store).unwrap_externref().clone().unwrap();
let s: &String = r.downcast(&store).unwrap();
assert_eq!(s.clone(), "hello".to_string());

// Initialize with a non-null externref.
let value = Value::ExternRef(Some(Arc::new(ExternRef::new(&mut store, 42_i32))));
let global = Global::new(&mut store, value);

let r = global.get(&mut store).unwrap_externref().clone().unwrap();
let u: &i32 = r.downcast(&store).unwrap();
assert_eq!(*u, 42_i32);

Ok(())
}

#[universal_test]
fn function_new_dynamic() -> Result<(), String> {
let mut store = Store::default();
Expand Down