Make FuncRef nullable at the API level

This commit is contained in:
Mark McCaskey
2021-02-01 09:41:18 -08:00
parent e013581fda
commit 63f277bce4
6 changed files with 54 additions and 22 deletions

View File

@@ -118,7 +118,7 @@ fn main() -> anyhow::Result<()> {
); );
// Now demonstarte that the function we grew the table with is actually in the table. // Now demonstarte that the function we grew the table with is actually in the table.
for table_index in 3..6 { for table_index in 3..6 {
if let Value::FuncRef(f) = guest_table.get(table_index as _).unwrap() { if let Value::FuncRef(Some(f)) = guest_table.get(table_index as _).unwrap() {
let result = f.call(&[Value::I32(1), Value::I32(9)])?; let result = f.call(&[Value::I32(1), Value::I32(9)])?;
assert_eq!(result[0], Value::I32(10)); assert_eq!(result[0], Value::I32(10));
} else { } else {
@@ -140,7 +140,7 @@ fn main() -> anyhow::Result<()> {
// Now demonstrate that the host and guest see the same table and that both // Now demonstrate that the host and guest see the same table and that both
// get the same result. // get the same result.
for table_index in 3..6 { for table_index in 3..6 {
if let Value::FuncRef(f) = guest_table.get(table_index as _).unwrap() { if let Value::FuncRef(Some(f)) = guest_table.get(table_index as _).unwrap() {
let result = f.call(&[Value::I32(1), Value::I32(9)])?; let result = f.call(&[Value::I32(1), Value::I32(9)])?;
assert_eq!(result[0], Value::I32(10)); assert_eq!(result[0], Value::I32(10));
} else { } else {

View File

@@ -19,9 +19,10 @@ pub type Val = Value<Function>;
impl StoreObject for Val { impl StoreObject for Val {
fn comes_from_same_store(&self, store: &Store) -> bool { fn comes_from_same_store(&self, store: &Store) -> bool {
match self { match self {
Self::FuncRef(f) => Store::same(store, f.store()), Self::FuncRef(None) => true,
Self::FuncRef(Some(f)) => Store::same(store, f.store()),
Self::ExternRef(ExternRef::Ref(_)) | Self::ExternRef(ExternRef::Other(_)) => false, Self::ExternRef(ExternRef::Ref(_)) | Self::ExternRef(ExternRef::Other(_)) => false,
Self::ExternRef(ExternRef::Null) => true, Self::ExternRef(ExternRef::Null) => todo!("update this code"),
Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true, Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true,
} }
} }
@@ -29,7 +30,7 @@ impl StoreObject for Val {
impl From<Function> for Val { impl From<Function> for Val {
fn from(val: Function) -> Self { fn from(val: Function) -> Self {
Self::FuncRef(val) Self::FuncRef(Some(val))
} }
} }
@@ -60,21 +61,29 @@ impl ValFuncRef for Val {
return Err(RuntimeError::new("cross-`Store` values are not supported")); return Err(RuntimeError::new("cross-`Store` values are not supported"));
} }
Ok(match self { Ok(match self {
Self::ExternRef(ExternRef::Null) => wasmer_vm::VMCallerCheckedAnyfunc { Self::ExternRef(ExternRef::Null) => todo!("Extern ref not yet implemented"), /*wasmer_vm::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(),
type_index: wasmer_vm::VMSharedSignatureIndex::default(),
vmctx: wasmer_vm::VMFunctionEnvironment {
host_env: ptr::null_mut(),
},
},*/
Self::FuncRef(None) => wasmer_vm::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(), func_ptr: ptr::null(),
type_index: wasmer_vm::VMSharedSignatureIndex::default(), type_index: wasmer_vm::VMSharedSignatureIndex::default(),
vmctx: wasmer_vm::VMFunctionEnvironment { vmctx: wasmer_vm::VMFunctionEnvironment {
host_env: ptr::null_mut(), host_env: ptr::null_mut(),
}, },
}, },
Self::FuncRef(f) => f.checked_anyfunc(), Self::FuncRef(Some(f)) => f.checked_anyfunc(),
_ => return Err(RuntimeError::new("val is not funcref")), _ => return Err(RuntimeError::new("val is not reference")),
}) })
} }
fn from_checked_anyfunc(item: wasmer_vm::VMCallerCheckedAnyfunc, store: &Store) -> Self { fn from_checked_anyfunc(item: wasmer_vm::VMCallerCheckedAnyfunc, store: &Store) -> Self {
// TODO: use `is_null()`?
if item.type_index == wasmer_vm::VMSharedSignatureIndex::default() { if item.type_index == wasmer_vm::VMSharedSignatureIndex::default() {
return Self::ExternRef(ExternRef::Null); return Self::FuncRef(None);
} }
let signature = store let signature = store
.engine() .engine()
@@ -96,7 +105,7 @@ impl ValFuncRef for Val {
}, },
}; };
let f = Function::from_vm_export(store, export); let f = Function::from_vm_export(store, export);
Self::FuncRef(f) Self::FuncRef(Some(f))
} }
fn into_table_reference( fn into_table_reference(
@@ -107,16 +116,28 @@ impl ValFuncRef for Val {
return Err(RuntimeError::new("cross-`Store` values are not supported")); return Err(RuntimeError::new("cross-`Store` values are not supported"));
} }
Ok(match self { Ok(match self {
Self::ExternRef(ExternRef::Null) => wasmer_vm::TableReference::FuncRef(wasmer_vm::VMCallerCheckedAnyfunc { Self::ExternRef(ExternRef::Null) =>
/*wasmer_vm::TableReference::FuncRef(wasmer_vm::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(), func_ptr: ptr::null(),
type_index: wasmer_vm::VMSharedSignatureIndex::default(), type_index: wasmer_vm::VMSharedSignatureIndex::default(),
vmctx: wasmer_vm::VMFunctionEnvironment { vmctx: wasmer_vm::VMFunctionEnvironment {
host_env: ptr::null_mut(), host_env: ptr::null_mut(),
}, },
}), }),*/
// existing code uses `ExtenRef` for null pointers // existing code uses `ExtenRef` for null pointers
//todo!("extern ref not yet supported"), {
Self::FuncRef(f) => wasmer_vm::TableReference::FuncRef(f.checked_anyfunc()), todo!("extern ref not yet supported")
}
Self::FuncRef(None) => {
wasmer_vm::TableReference::FuncRef(wasmer_vm::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(),
type_index: wasmer_vm::VMSharedSignatureIndex::default(),
vmctx: wasmer_vm::VMFunctionEnvironment {
host_env: ptr::null_mut(),
},
})
}
Self::FuncRef(Some(f)) => wasmer_vm::TableReference::FuncRef(f.checked_anyfunc()),
_ => return Err(RuntimeError::new("val is not reference")), _ => return Err(RuntimeError::new("val is not reference")),
}) })
} }

View File

@@ -67,7 +67,7 @@ fn table_new() -> Result<()> {
maximum: None, maximum: None,
}; };
let f = Function::new_native(&store, || {}); let f = Function::new_native(&store, || {});
let table = Table::new(&store, table_type, Value::FuncRef(f))?; let table = Table::new(&store, table_type, Value::FuncRef(Some(f)))?;
assert_eq!(*table.ty(), table_type); assert_eq!(*table.ty(), table_type);
// Anyrefs not yet supported // Anyrefs not yet supported
@@ -92,7 +92,7 @@ fn table_get() -> Result<()> {
maximum: Some(1), maximum: Some(1),
}; };
let f = Function::new_native(&store, |num: i32| num + 1); let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(f.clone()))?; let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
assert_eq!(*table.ty(), table_type); assert_eq!(*table.ty(), table_type);
let _elem = table.get(0).unwrap(); let _elem = table.get(0).unwrap();
// assert_eq!(elem.funcref().unwrap(), f); // assert_eq!(elem.funcref().unwrap(), f);
@@ -115,13 +115,13 @@ fn table_grow() -> Result<()> {
maximum: Some(10), maximum: Some(10),
}; };
let f = Function::new_native(&store, |num: i32| num + 1); let f = Function::new_native(&store, |num: i32| num + 1);
let table = Table::new(&store, table_type, Value::FuncRef(f.clone()))?; let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?;
// Growing to a bigger maximum should return None // Growing to a bigger maximum should return None
let old_len = table.grow(12, Value::FuncRef(f.clone())); let old_len = table.grow(12, Value::FuncRef(Some(f.clone())));
assert!(old_len.is_err()); assert!(old_len.is_err());
// Growing to a bigger maximum should return None // Growing to a bigger maximum should return None
let old_len = table.grow(5, Value::FuncRef(f.clone()))?; let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?;
assert_eq!(old_len, 0); assert_eq!(old_len, 0);
Ok(()) Ok(())

View File

@@ -762,6 +762,17 @@ pub struct VMCallerCheckedAnyfunc {
// If more elements are added here, remember to add offset_of tests below! // If more elements are added here, remember to add offset_of tests below!
} }
impl VMCallerCheckedAnyfunc {
/// Check if this anyfunc is a null funcref.
// TODO: we probably want to clean this up before shipping, maybe actually add
// an extra layer of indirection
pub fn is_null(&self) -> bool {
self.func_ptr == std::ptr::null()
&& self.type_index == VMSharedSignatureIndex::default()
&& self.vmctx.is_null()
}
}
#[cfg(test)] #[cfg(test)]
mod test_vmcaller_checked_anyfunc { mod test_vmcaller_checked_anyfunc {
use super::VMCallerCheckedAnyfunc; use super::VMCallerCheckedAnyfunc;

View File

@@ -31,7 +31,7 @@ pub enum Value<T> {
ExternRef(ExternRef), ExternRef(ExternRef),
/// A first-class reference to a WebAssembly function. /// A first-class reference to a WebAssembly function.
FuncRef(T), FuncRef(Option<T>),
/// A 128-bit number /// A 128-bit number
V128(u128), V128(u128),
@@ -120,7 +120,7 @@ impl<T> Value<T> {
(I64(i64) i64 unwrap_i64 *e) (I64(i64) i64 unwrap_i64 *e)
(F32(f32) f32 unwrap_f32 *e) (F32(f32) f32 unwrap_f32 *e)
(F64(f64) f64 unwrap_f64 *e) (F64(f64) f64 unwrap_f64 *e)
(FuncRef(&T) funcref unwrap_funcref e) (FuncRef(&Option<T>) funcref unwrap_funcref e)
(V128(u128) v128 unwrap_v128 *e) (V128(u128) v128 unwrap_v128 *e)
} }

View File

@@ -23,7 +23,7 @@ pub fn spectest_importobject(store: &Store) -> ImportObject {
let global_f64 = Global::new(store, Val::F64(f64::from_bits(0x4084_d000_0000_0000))); let global_f64 = Global::new(store, Val::F64(f64::from_bits(0x4084_d000_0000_0000)));
let ty = TableType::new(ValType::FuncRef, 10, Some(20)); let ty = TableType::new(ValType::FuncRef, 10, Some(20));
let table = Table::new(store, ty, Val::ExternRef(ExternRef::Null)).unwrap(); let table = Table::new(store, ty, Val::FuncRef(None)).unwrap();
let ty = MemoryType::new(1, Some(2), false); let ty = MemoryType::new(1, Some(2), false);
let memory = Memory::new(store, ty).unwrap(); let memory = Memory::new(store, ty).unwrap();