From 63f277bce4881c7776c2bc50d43a47aed505b564 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 1 Feb 2021 09:41:18 -0800 Subject: [PATCH] Make FuncRef nullable at the API level --- examples/table.rs | 4 +-- lib/api/src/types.rs | 45 +++++++++++++++++++++++++--------- lib/api/tests/externals.rs | 10 ++++---- lib/vm/src/vmcontext.rs | 11 +++++++++ lib/wasmer-types/src/values.rs | 4 +-- tests/lib/wast/src/spectest.rs | 2 +- 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/examples/table.rs b/examples/table.rs index 90012a6b3..2d0dde454 100644 --- a/examples/table.rs +++ b/examples/table.rs @@ -118,7 +118,7 @@ fn main() -> anyhow::Result<()> { ); // Now demonstarte that the function we grew the table with is actually in the table. 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)])?; assert_eq!(result[0], Value::I32(10)); } else { @@ -140,7 +140,7 @@ fn main() -> anyhow::Result<()> { // Now demonstrate that the host and guest see the same table and that both // get the same result. 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)])?; assert_eq!(result[0], Value::I32(10)); } else { diff --git a/lib/api/src/types.rs b/lib/api/src/types.rs index e48073197..ea09ed64b 100644 --- a/lib/api/src/types.rs +++ b/lib/api/src/types.rs @@ -19,9 +19,10 @@ pub type Val = Value; impl StoreObject for Val { fn comes_from_same_store(&self, store: &Store) -> bool { 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::Null) => true, + Self::ExternRef(ExternRef::Null) => todo!("update this code"), Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true, } } @@ -29,7 +30,7 @@ impl StoreObject for Val { impl From for Val { 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")); } 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(), type_index: wasmer_vm::VMSharedSignatureIndex::default(), vmctx: wasmer_vm::VMFunctionEnvironment { host_env: ptr::null_mut(), }, }, - Self::FuncRef(f) => f.checked_anyfunc(), - _ => return Err(RuntimeError::new("val is not funcref")), + Self::FuncRef(Some(f)) => f.checked_anyfunc(), + _ => return Err(RuntimeError::new("val is not reference")), }) } fn from_checked_anyfunc(item: wasmer_vm::VMCallerCheckedAnyfunc, store: &Store) -> Self { + // TODO: use `is_null()`? if item.type_index == wasmer_vm::VMSharedSignatureIndex::default() { - return Self::ExternRef(ExternRef::Null); + return Self::FuncRef(None); } let signature = store .engine() @@ -96,7 +105,7 @@ impl ValFuncRef for Val { }, }; let f = Function::from_vm_export(store, export); - Self::FuncRef(f) + Self::FuncRef(Some(f)) } fn into_table_reference( @@ -107,16 +116,28 @@ impl ValFuncRef for Val { return Err(RuntimeError::new("cross-`Store` values are not supported")); } 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(), type_index: wasmer_vm::VMSharedSignatureIndex::default(), vmctx: wasmer_vm::VMFunctionEnvironment { host_env: ptr::null_mut(), }, - }), + }),*/ // 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")), }) } diff --git a/lib/api/tests/externals.rs b/lib/api/tests/externals.rs index 5dec277bc..767272e87 100644 --- a/lib/api/tests/externals.rs +++ b/lib/api/tests/externals.rs @@ -67,7 +67,7 @@ fn table_new() -> Result<()> { maximum: None, }; 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); // Anyrefs not yet supported @@ -92,7 +92,7 @@ fn table_get() -> Result<()> { maximum: Some(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); let _elem = table.get(0).unwrap(); // assert_eq!(elem.funcref().unwrap(), f); @@ -115,13 +115,13 @@ fn table_grow() -> Result<()> { maximum: Some(10), }; 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 - 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()); // 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); Ok(()) diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index 55f4dfa8b..2af9fcdcc 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -762,6 +762,17 @@ pub struct VMCallerCheckedAnyfunc { // 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)] mod test_vmcaller_checked_anyfunc { use super::VMCallerCheckedAnyfunc; diff --git a/lib/wasmer-types/src/values.rs b/lib/wasmer-types/src/values.rs index 5722d3ca6..7ae825db6 100644 --- a/lib/wasmer-types/src/values.rs +++ b/lib/wasmer-types/src/values.rs @@ -31,7 +31,7 @@ pub enum Value { ExternRef(ExternRef), /// A first-class reference to a WebAssembly function. - FuncRef(T), + FuncRef(Option), /// A 128-bit number V128(u128), @@ -120,7 +120,7 @@ impl Value { (I64(i64) i64 unwrap_i64 *e) (F32(f32) f32 unwrap_f32 *e) (F64(f64) f64 unwrap_f64 *e) - (FuncRef(&T) funcref unwrap_funcref e) + (FuncRef(&Option) funcref unwrap_funcref e) (V128(u128) v128 unwrap_v128 *e) } diff --git a/tests/lib/wast/src/spectest.rs b/tests/lib/wast/src/spectest.rs index 7e956c713..28d13995d 100644 --- a/tests/lib/wast/src/spectest.rs +++ b/tests/lib/wast/src/spectest.rs @@ -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 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 memory = Memory::new(store, ty).unwrap();