wasmtime: Initial, partial support for externref

This is enough to get an `externref -> externref` identity function
passing.

However, `externref`s that are dropped by compiled Wasm code are (safely)
leaked. Follow up work will leverage cranelift's stack maps to resolve this
issue.
This commit is contained in:
Nick Fitzgerald
2020-05-22 17:12:45 -07:00
parent 137e182750
commit a8ee0554a9
41 changed files with 545 additions and 376 deletions

View File

@@ -205,6 +205,7 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
// Still working on implementing these. See #929. // Still working on implementing these. See #929.
("reference_types", "table_copy_on_imported_tables") => return false, ("reference_types", "table_copy_on_imported_tables") => return false,
("reference_types", "externref_id_function") => return false,
("reference_types", _) => return true, ("reference_types", _) => return true,
_ => {} _ => {}

View File

@@ -96,7 +96,7 @@ pub fn declare_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[no_mangle] #[no_mangle]
pub extern fn #as_ref(a: &#ty) -> Box<crate::wasm_ref_t> { pub extern fn #as_ref(a: &#ty) -> Box<crate::wasm_ref_t> {
let r = a.externref(); let r = Some(a.externref());
Box::new(crate::wasm_ref_t { r }) Box::new(crate::wasm_ref_t { r })
} }

View File

@@ -1,10 +1,10 @@
use crate::wasm_config_t; use crate::wasm_config_t;
use wasmtime::{Engine, HostRef}; use wasmtime::Engine;
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasm_engine_t { pub struct wasm_engine_t {
pub(crate) engine: HostRef<Engine>, pub(crate) engine: Engine,
} }
wasmtime_c_api_macros::declare_own!(wasm_engine_t); wasmtime_c_api_macros::declare_own!(wasm_engine_t);
@@ -22,7 +22,7 @@ pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
drop(env_logger::try_init()); drop(env_logger::try_init());
Box::new(wasm_engine_t { Box::new(wasm_engine_t {
engine: HostRef::new(Engine::default()), engine: Engine::default(),
}) })
} }
@@ -30,6 +30,6 @@ pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
pub extern "C" fn wasm_engine_new_with_config(c: Box<wasm_config_t>) -> Box<wasm_engine_t> { pub extern "C" fn wasm_engine_new_with_config(c: Box<wasm_config_t>) -> Box<wasm_engine_t> {
let config = c.config; let config = c.config;
Box::new(wasm_engine_t { Box::new(wasm_engine_t {
engine: HostRef::new(Engine::new(&config)), engine: Engine::new(&config),
}) })
} }

View File

@@ -1,6 +1,6 @@
use crate::{wasm_name_t, wasm_trap_t}; use crate::{wasm_name_t, wasm_trap_t};
use anyhow::{anyhow, Error, Result}; use anyhow::{anyhow, Error, Result};
use wasmtime::Trap; use wasmtime::{Store, Trap};
#[repr(C)] #[repr(C)]
pub struct wasmtime_error_t { pub struct wasmtime_error_t {
@@ -10,8 +10,8 @@ pub struct wasmtime_error_t {
wasmtime_c_api_macros::declare_own!(wasmtime_error_t); wasmtime_c_api_macros::declare_own!(wasmtime_error_t);
impl wasmtime_error_t { impl wasmtime_error_t {
pub(crate) fn to_trap(self) -> Box<wasm_trap_t> { pub(crate) fn to_trap(self, store: &Store) -> Box<wasm_trap_t> {
Box::new(wasm_trap_t::new(Trap::from(self.error))) Box::new(wasm_trap_t::new(store, Trap::from(self.error)))
} }
} }

View File

@@ -18,12 +18,12 @@ pub(crate) enum ExternHost {
} }
impl wasm_extern_t { impl wasm_extern_t {
fn externref(&self) -> wasmtime::ExternRef { pub(crate) fn externref(&self) -> wasmtime::ExternRef {
match &self.which { match &self.which {
ExternHost::Func(f) => f.externref(), ExternHost::Func(f) => f.clone().into(),
ExternHost::Global(f) => f.externref(), ExternHost::Global(f) => f.clone().into(),
ExternHost::Memory(f) => f.externref(), ExternHost::Memory(f) => f.clone().into(),
ExternHost::Table(f) => f.externref(), ExternHost::Table(f) => f.clone().into(),
} }
} }
} }

View File

@@ -71,7 +71,7 @@ impl wasm_func_t {
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.func().externref() self.func().clone().into()
} }
} }
@@ -90,7 +90,7 @@ fn create_function(
ty: &wasm_functype_t, ty: &wasm_functype_t,
func: impl Fn(Caller<'_>, *const wasm_val_t, *mut wasm_val_t) -> Option<Box<wasm_trap_t>> + 'static, func: impl Fn(Caller<'_>, *const wasm_val_t, *mut wasm_val_t) -> Option<Box<wasm_trap_t>> + 'static,
) -> Box<wasm_func_t> { ) -> Box<wasm_func_t> {
let store = &store.store.borrow(); let store = &store.store;
let ty = ty.ty().ty.clone(); let ty = ty.ty().ty.clone();
let func = Func::new(store, ty, move |caller, params, results| { let func = Func::new(store, ty, move |caller, params, results| {
let params = params let params = params
@@ -107,7 +107,7 @@ fn create_function(
} }
Ok(()) Ok(())
}); });
Box::new(HostRef::new(func).into()) Box::new(HostRef::new(store, func).into())
} }
#[no_mangle] #[no_mangle]
@@ -182,7 +182,7 @@ pub unsafe extern "C" fn wasm_func_call(
&mut trap, &mut trap,
); );
match error { match error {
Some(err) => Box::into_raw(err.to_trap()), Some(err) => Box::into_raw(err.to_trap(&wasm_func.ext.externref().store().unwrap())),
None => trap, None => trap,
} }
} }
@@ -210,6 +210,7 @@ fn _wasmtime_func_call(
results: &mut [wasm_val_t], results: &mut [wasm_val_t],
trap_ptr: &mut *mut wasm_trap_t, trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let store = &func.ext.externref().store().unwrap();
let func = func.func().borrow(); let func = func.func().borrow();
if results.len() != func.result_arity() { if results.len() != func.result_arity() {
return Some(Box::new(anyhow!("wrong number of results provided").into())); return Some(Box::new(anyhow!("wrong number of results provided").into()));
@@ -230,7 +231,7 @@ fn _wasmtime_func_call(
} }
Ok(Err(trap)) => match trap.downcast::<Trap>() { Ok(Err(trap)) => match trap.downcast::<Trap>() {
Ok(trap) => { Ok(trap) => {
*trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(trap))); *trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(store, trap)));
None None
} }
Err(err) => Some(Box::new(err.into())), Err(err) => Some(Box::new(err.into())),
@@ -243,7 +244,7 @@ fn _wasmtime_func_call(
} else { } else {
Trap::new("rust panic happened") Trap::new("rust panic happened")
}; };
let trap = Box::new(wasm_trap_t::new(trap)); let trap = Box::new(wasm_trap_t::new(store, trap));
*trap_ptr = Box::into_raw(trap); *trap_ptr = Box::into_raw(trap);
None None
} }
@@ -277,11 +278,12 @@ pub unsafe extern "C" fn wasmtime_caller_export_get(
) -> Option<Box<wasm_extern_t>> { ) -> Option<Box<wasm_extern_t>> {
let name = str::from_utf8(name.as_slice()).ok()?; let name = str::from_utf8(name.as_slice()).ok()?;
let export = caller.caller.get_export(name)?; let export = caller.caller.get_export(name)?;
let store = caller.caller.store();
let which = match export { let which = match export {
Extern::Func(f) => ExternHost::Func(HostRef::new(f)), Extern::Func(f) => ExternHost::Func(HostRef::new(&store, f)),
Extern::Global(g) => ExternHost::Global(HostRef::new(g)), Extern::Global(g) => ExternHost::Global(HostRef::new(&store, g)),
Extern::Memory(m) => ExternHost::Memory(HostRef::new(m)), Extern::Memory(m) => ExternHost::Memory(HostRef::new(&store, m)),
Extern::Table(t) => ExternHost::Table(HostRef::new(t)), Extern::Table(t) => ExternHost::Table(HostRef::new(&store, t)),
}; };
Some(Box::new(wasm_extern_t { which })) Some(Box::new(wasm_extern_t { which }))
} }

View File

@@ -27,7 +27,7 @@ impl wasm_global_t {
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.global().externref() self.global().clone().into()
} }
} }
@@ -54,11 +54,11 @@ pub extern "C" fn wasmtime_global_new(
val: &wasm_val_t, val: &wasm_val_t,
ret: &mut *mut wasm_global_t, ret: &mut *mut wasm_global_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let global = Global::new(&store.store.borrow(), gt.ty().ty.clone(), val.val()); let global = Global::new(&store.store, gt.ty().ty.clone(), val.val());
handle_result(global, |global| { handle_result(global, |global| {
*ret = Box::into_raw(Box::new(wasm_global_t { *ret = Box::into_raw(Box::new(wasm_global_t {
ext: wasm_extern_t { ext: wasm_extern_t {
which: ExternHost::Global(HostRef::new(global)), which: ExternHost::Global(HostRef::new(&store.store, global)),
}, },
})); }));
}) })

View File

@@ -16,14 +16,15 @@ wasmtime_c_api_macros::declare_ref!(wasm_instance_t);
impl wasm_instance_t { impl wasm_instance_t {
pub(crate) fn new(instance: Instance) -> wasm_instance_t { pub(crate) fn new(instance: Instance) -> wasm_instance_t {
let store = instance.store().clone();
wasm_instance_t { wasm_instance_t {
instance: HostRef::new(instance), instance: HostRef::new(&store, instance),
exports_cache: RefCell::new(None), exports_cache: RefCell::new(None),
} }
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.instance.externref() self.instance.clone().into()
} }
} }
@@ -34,12 +35,12 @@ pub unsafe extern "C" fn wasm_instance_new(
imports: *const Box<wasm_extern_t>, imports: *const Box<wasm_extern_t>,
result: Option<&mut *mut wasm_trap_t>, result: Option<&mut *mut wasm_trap_t>,
) -> Option<Box<wasm_instance_t>> { ) -> Option<Box<wasm_instance_t>> {
let store = &store.store.borrow(); let store = &store.store;
let module = &wasm_module.module.borrow(); let module = &wasm_module.module.borrow();
if !Store::same(&store, module.store()) { if !Store::same(&store, module.store()) {
if let Some(result) = result { if let Some(result) = result {
let trap = Trap::new("wasm_store_t must match store in wasm_module_t"); let trap = Trap::new("wasm_store_t must match store in wasm_module_t");
let trap = Box::new(wasm_trap_t::new(trap)); let trap = Box::new(wasm_trap_t::new(store, trap));
*result = Box::into_raw(trap); *result = Box::into_raw(trap);
} }
return None; return None;
@@ -58,7 +59,7 @@ pub unsafe extern "C" fn wasm_instance_new(
assert!(trap.is_null()); assert!(trap.is_null());
assert!(instance.is_null()); assert!(instance.is_null());
if let Some(result) = result { if let Some(result) = result {
*result = Box::into_raw(err.to_trap()); *result = Box::into_raw(err.to_trap(store));
} }
None None
} }
@@ -111,10 +112,16 @@ fn _wasmtime_instance_new(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let module = &module.module.borrow(); let module = &module.module.borrow();
handle_instantiate(Instance::new(module, &imports), instance_ptr, trap_ptr) handle_instantiate(
module.store(),
Instance::new(module, &imports),
instance_ptr,
trap_ptr,
)
} }
pub fn handle_instantiate( pub fn handle_instantiate(
store: &Store,
instance: Result<Instance>, instance: Result<Instance>,
instance_ptr: &mut *mut wasm_instance_t, instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t, trap_ptr: &mut *mut wasm_trap_t,
@@ -130,7 +137,7 @@ pub fn handle_instantiate(
} }
Err(e) => match e.downcast::<Trap>() { Err(e) => match e.downcast::<Trap>() {
Ok(trap) => { Ok(trap) => {
write(trap_ptr, wasm_trap_t::new(trap)); write(trap_ptr, wasm_trap_t::new(store, trap));
None None
} }
Err(e) => Some(Box::new(e.into())), Err(e) => Some(Box::new(e.into())),
@@ -146,10 +153,10 @@ pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wa
instance instance
.exports() .exports()
.map(|e| match e.into_extern() { .map(|e| match e.into_extern() {
Extern::Func(f) => ExternHost::Func(HostRef::new(f)), Extern::Func(f) => ExternHost::Func(HostRef::new(instance.store(), f)),
Extern::Global(f) => ExternHost::Global(HostRef::new(f)), Extern::Global(f) => ExternHost::Global(HostRef::new(instance.store(), f)),
Extern::Memory(f) => ExternHost::Memory(HostRef::new(f)), Extern::Memory(f) => ExternHost::Memory(HostRef::new(instance.store(), f)),
Extern::Table(f) => ExternHost::Table(HostRef::new(f)), Extern::Table(f) => ExternHost::Table(HostRef::new(instance.store(), f)),
}) })
.collect() .collect()
}); });

View File

@@ -12,7 +12,7 @@ pub struct wasmtime_linker_t {
#[no_mangle] #[no_mangle]
pub extern "C" fn wasmtime_linker_new(store: &wasm_store_t) -> Box<wasmtime_linker_t> { pub extern "C" fn wasmtime_linker_new(store: &wasm_store_t) -> Box<wasmtime_linker_t> {
Box::new(wasmtime_linker_t { Box::new(wasmtime_linker_t {
linker: Linker::new(&store.store.borrow()), linker: Linker::new(&store.store),
}) })
} }
@@ -87,7 +87,7 @@ pub unsafe extern "C" fn wasmtime_linker_instantiate(
trap_ptr: &mut *mut wasm_trap_t, trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let result = linker.linker.instantiate(&module.module.borrow()); let result = linker.linker.instantiate(&module.module.borrow());
super::instance::handle_instantiate(result, instance_ptr, trap_ptr) super::instance::handle_instantiate(linker.linker.store(), result, instance_ptr, trap_ptr)
} }
#[no_mangle] #[no_mangle]
@@ -116,6 +116,6 @@ pub unsafe extern "C" fn wasmtime_linker_get_default(
Err(_) => return bad_utf8(), Err(_) => return bad_utf8(),
}; };
handle_result(linker.get_default(name), |f| { handle_result(linker.get_default(name), |f| {
*func = Box::into_raw(Box::new(HostRef::new(f).into())) *func = Box::into_raw(Box::new(HostRef::new(linker.store(), f).into()))
}) })
} }

View File

@@ -27,7 +27,7 @@ impl wasm_memory_t {
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.memory().externref() self.memory().clone().into()
} }
} }
@@ -36,7 +36,7 @@ pub extern "C" fn wasm_memory_new(
store: &wasm_store_t, store: &wasm_store_t,
mt: &wasm_memorytype_t, mt: &wasm_memorytype_t,
) -> Box<wasm_memory_t> { ) -> Box<wasm_memory_t> {
let memory = HostRef::new(Memory::new(&store.store.borrow(), mt.ty().ty.clone())); let memory = HostRef::new(&store.store, Memory::new(&store.store, mt.ty().ty.clone()));
Box::new(wasm_memory_t { Box::new(wasm_memory_t {
ext: wasm_extern_t { ext: wasm_extern_t {
which: ExternHost::Memory(memory), which: ExternHost::Memory(memory),

View File

@@ -16,7 +16,7 @@ wasmtime_c_api_macros::declare_ref!(wasm_module_t);
impl wasm_module_t { impl wasm_module_t {
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.module.externref() self.module.clone().into()
} }
} }
@@ -42,7 +42,7 @@ pub extern "C" fn wasmtime_module_new(
ret: &mut *mut wasm_module_t, ret: &mut *mut wasm_module_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice(); let binary = binary.as_slice();
let store = &store.store.borrow(); let store = &store.store;
handle_result(Module::from_binary(store, binary), |module| { handle_result(Module::from_binary(store, binary), |module| {
let imports = module let imports = module
.imports() .imports()
@@ -53,7 +53,7 @@ pub extern "C" fn wasmtime_module_new(
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty())) .map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let module = Box::new(wasm_module_t { let module = Box::new(wasm_module_t {
module: HostRef::new(module), module: HostRef::new(store, module),
imports, imports,
exports, exports,
}); });
@@ -72,7 +72,7 @@ pub extern "C" fn wasmtime_module_validate(
binary: &wasm_byte_vec_t, binary: &wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice(); let binary = binary.as_slice();
let store = &store.store.borrow(); let store = &store.store;
handle_result(Module::validate(store, binary), |()| {}) handle_result(Module::validate(store, binary), |()| {})
} }

View File

@@ -5,7 +5,7 @@ use wasmtime::ExternRef;
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasm_ref_t { pub struct wasm_ref_t {
pub(crate) r: ExternRef, pub(crate) r: Option<ExternRef>,
} }
wasmtime_c_api_macros::declare_own!(wasm_ref_t); wasmtime_c_api_macros::declare_own!(wasm_ref_t);
@@ -17,7 +17,11 @@ pub extern "C" fn wasm_ref_copy(r: &wasm_ref_t) -> Box<wasm_ref_t> {
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool { pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool {
a.r.ptr_eq(&b.r) match (a.r.as_ref(), b.r.as_ref()) {
(Some(a), Some(b)) => a.ptr_eq(b),
(None, None) => true,
_ => false,
}
} }
pub(crate) fn get_host_info(r: &ExternRef) -> *mut c_void { pub(crate) fn get_host_info(r: &ExternRef) -> *mut c_void {
@@ -25,6 +29,7 @@ pub(crate) fn get_host_info(r: &ExternRef) -> *mut c_void {
Some(info) => info, Some(info) => info,
None => return std::ptr::null_mut(), None => return std::ptr::null_mut(),
}; };
let host_info = host_info.borrow();
match host_info.downcast_ref::<HostInfoState>() { match host_info.downcast_ref::<HostInfoState>() {
Some(state) => state.info, Some(state) => state.info,
None => std::ptr::null_mut(), None => std::ptr::null_mut(),
@@ -33,7 +38,8 @@ pub(crate) fn get_host_info(r: &ExternRef) -> *mut c_void {
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_ref_get_host_info(a: &wasm_ref_t) -> *mut c_void { pub extern "C" fn wasm_ref_get_host_info(a: &wasm_ref_t) -> *mut c_void {
get_host_info(&a.r) a.r.as_ref()
.map_or(std::ptr::null_mut(), |r| get_host_info(r))
} }
pub(crate) fn set_host_info( pub(crate) fn set_host_info(
@@ -51,7 +57,7 @@ pub(crate) fn set_host_info(
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_ref_set_host_info(a: &wasm_ref_t, info: *mut c_void) { pub extern "C" fn wasm_ref_set_host_info(a: &wasm_ref_t, info: *mut c_void) {
set_host_info(&a.r, info, None) a.r.as_ref().map(|r| set_host_info(r, info, None));
} }
#[no_mangle] #[no_mangle]
@@ -60,5 +66,5 @@ pub extern "C" fn wasm_ref_set_host_info_with_finalizer(
info: *mut c_void, info: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>, finalizer: Option<extern "C" fn(*mut c_void)>,
) { ) {
set_host_info(&a.r, info, finalizer) a.r.as_ref().map(|r| set_host_info(r, info, finalizer));
} }

View File

@@ -1,10 +1,10 @@
use crate::wasm_engine_t; use crate::wasm_engine_t;
use wasmtime::{HostRef, InterruptHandle, Store}; use wasmtime::{InterruptHandle, Store};
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct wasm_store_t { pub struct wasm_store_t {
pub(crate) store: HostRef<Store>, pub(crate) store: Store,
} }
wasmtime_c_api_macros::declare_own!(wasm_store_t); wasmtime_c_api_macros::declare_own!(wasm_store_t);
@@ -13,7 +13,7 @@ wasmtime_c_api_macros::declare_own!(wasm_store_t);
pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> { pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
let engine = &engine.engine; let engine = &engine.engine;
Box::new(wasm_store_t { Box::new(wasm_store_t {
store: HostRef::new(Store::new(&engine.borrow())), store: Store::new(&engine),
}) })
} }
@@ -29,7 +29,7 @@ pub extern "C" fn wasmtime_interrupt_handle_new(
store: &wasm_store_t, store: &wasm_store_t,
) -> Option<Box<wasmtime_interrupt_handle_t>> { ) -> Option<Box<wasmtime_interrupt_handle_t>> {
Some(Box::new(wasmtime_interrupt_handle_t { Some(Box::new(wasmtime_interrupt_handle_t {
handle: store.store.borrow().interrupt_handle().ok()?, handle: store.store.interrupt_handle().ok()?,
})) }))
} }

View File

@@ -1,7 +1,7 @@
use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t}; use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t, ExternHost}; use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t, ExternHost};
use std::ptr; use std::ptr;
use wasmtime::{ExternRef, HostRef, Table, Val}; use wasmtime::{HostRef, Table, Val};
#[derive(Clone)] #[derive(Clone)]
#[repr(transparent)] #[repr(transparent)]
@@ -29,7 +29,7 @@ impl wasm_table_t {
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.table().externref() self.table().clone().into()
} }
} }
@@ -41,12 +41,12 @@ pub extern "C" fn wasm_table_new(
) -> Option<Box<wasm_table_t>> { ) -> Option<Box<wasm_table_t>> {
let init: Val = match init { let init: Val = match init {
Some(init) => init.r.into(), Some(init) => init.r.into(),
None => Val::ExternRef(ExternRef::Null), None => Val::ExternRef(None),
}; };
let table = Table::new(&store.store.borrow(), tt.ty().ty.clone(), init).ok()?; let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
Some(Box::new(wasm_table_t { Some(Box::new(wasm_table_t {
ext: wasm_extern_t { ext: wasm_extern_t {
which: ExternHost::Table(HostRef::new(table)), which: ExternHost::Table(HostRef::new(&store.store, table)),
}, },
})) }))
} }
@@ -60,14 +60,14 @@ pub extern "C" fn wasmtime_funcref_table_new(
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let init: Val = match init { let init: Val = match init {
Some(val) => Val::FuncRef(val.func().borrow().clone()), Some(val) => Val::FuncRef(val.func().borrow().clone()),
None => Val::ExternRef(ExternRef::Null), None => Val::ExternRef(None),
}; };
handle_result( handle_result(
Table::new(&store.store.borrow(), tt.ty().ty.clone(), init), Table::new(&store.store, tt.ty().ty.clone(), init),
|table| { |table| {
*out = Box::into_raw(Box::new(wasm_table_t { *out = Box::into_raw(Box::new(wasm_table_t {
ext: wasm_extern_t { ext: wasm_extern_t {
which: ExternHost::Table(HostRef::new(table)), which: ExternHost::Table(HostRef::new(&store.store, table)),
}, },
})); }));
}, },
@@ -84,7 +84,7 @@ pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t { pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
match t.table().borrow().get(index) { match t.table().borrow().get(index) {
Some(val) => into_funcref(val), Some(val) => into_funcref(val),
None => into_funcref(Val::ExternRef(ExternRef::Null)), None => into_funcref(Val::ExternRef(None)),
} }
} }
@@ -98,8 +98,14 @@ pub extern "C" fn wasmtime_funcref_table_get(
Some(val) => { Some(val) => {
*ptr = match val { *ptr = match val {
// TODO: what do do about creating new `HostRef` handles here? // TODO: what do do about creating new `HostRef` handles here?
Val::FuncRef(f) => Box::into_raw(Box::new(HostRef::new(f).into())), Val::FuncRef(f) => {
Val::ExternRef(ExternRef::Null) => ptr::null_mut(), let store = match t.table().as_ref().store() {
None => return false,
Some(store) => store,
};
Box::into_raw(Box::new(HostRef::new(&store, f).into()))
}
Val::ExternRef(None) => ptr::null_mut(),
_ => return false, _ => return false,
}; };
} }
@@ -127,13 +133,13 @@ pub extern "C" fn wasmtime_funcref_table_set(
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let val = match val { let val = match val {
Some(val) => Val::FuncRef(val.func().borrow().clone()), Some(val) => Val::FuncRef(val.func().borrow().clone()),
None => Val::ExternRef(ExternRef::Null), None => Val::ExternRef(None),
}; };
handle_result(t.table().borrow().set(index, val), |()| {}) handle_result(t.table().borrow().set(index, val), |()| {})
} }
fn into_funcref(val: Val) -> *mut wasm_ref_t { fn into_funcref(val: Val) -> *mut wasm_ref_t {
if let Val::ExternRef(ExternRef::Null) = val { if let Val::ExternRef(None) = val {
return ptr::null_mut(); return ptr::null_mut();
} }
let externref = match val.externref() { let externref = match val.externref() {
@@ -148,7 +154,7 @@ unsafe fn from_funcref(r: *mut wasm_ref_t) -> Val {
if !r.is_null() { if !r.is_null() {
Box::from_raw(r).r.into() Box::from_raw(r).r.into()
} else { } else {
Val::ExternRef(ExternRef::Null) Val::ExternRef(None)
} }
} }
@@ -176,7 +182,7 @@ pub extern "C" fn wasmtime_funcref_table_grow(
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let val = match init { let val = match init {
Some(val) => Val::FuncRef(val.func().borrow().clone()), Some(val) => Val::FuncRef(val.func().borrow().clone()),
None => Val::ExternRef(ExternRef::Null), None => Val::ExternRef(None),
}; };
handle_result(t.table().borrow().grow(delta, val), |prev| { handle_result(t.table().borrow().grow(delta, val), |prev| {
if let Some(ptr) = prev_size { if let Some(ptr) = prev_size {

View File

@@ -1,6 +1,6 @@
use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t}; use crate::{wasm_frame_vec_t, wasm_instance_t, wasm_name_t, wasm_store_t};
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use wasmtime::{HostRef, Trap}; use wasmtime::{HostRef, Store, Trap};
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
@@ -11,14 +11,14 @@ pub struct wasm_trap_t {
wasmtime_c_api_macros::declare_ref!(wasm_trap_t); wasmtime_c_api_macros::declare_ref!(wasm_trap_t);
impl wasm_trap_t { impl wasm_trap_t {
pub(crate) fn new(trap: Trap) -> wasm_trap_t { pub(crate) fn new(store: &Store, trap: Trap) -> wasm_trap_t {
wasm_trap_t { wasm_trap_t {
trap: HostRef::new(trap), trap: HostRef::new(store, trap),
} }
} }
fn externref(&self) -> wasmtime::ExternRef { fn externref(&self) -> wasmtime::ExternRef {
self.trap.externref() self.trap.clone().into()
} }
} }
@@ -37,7 +37,7 @@ pub type wasm_message_t = wasm_name_t;
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_trap_new( pub extern "C" fn wasm_trap_new(
_store: &wasm_store_t, store: &wasm_store_t,
message: &wasm_message_t, message: &wasm_message_t,
) -> Box<wasm_trap_t> { ) -> Box<wasm_trap_t> {
let message = message.as_slice(); let message = message.as_slice();
@@ -46,7 +46,7 @@ pub extern "C" fn wasm_trap_new(
} }
let message = String::from_utf8_lossy(&message[..message.len() - 1]); let message = String::from_utf8_lossy(&message[..message.len() - 1]);
Box::new(wasm_trap_t { Box::new(wasm_trap_t {
trap: HostRef::new(Trap::new(message)), trap: HostRef::new(&store.store, Trap::new(message)),
}) })
} }

View File

@@ -282,7 +282,7 @@ pub unsafe extern "C" fn wasi_instance_new(
config: Box<wasi_config_t>, config: Box<wasi_config_t>,
trap: &mut *mut wasm_trap_t, trap: &mut *mut wasm_trap_t,
) -> Option<Box<wasi_instance_t>> { ) -> Option<Box<wasi_instance_t>> {
let store = &store.store.borrow(); let store = &store.store;
let result = match CStr::from_ptr(name).to_str().unwrap_or("") { let result = match CStr::from_ptr(name).to_str().unwrap_or("") {
"wasi_snapshot_preview1" => create_preview1_instance(store, *config), "wasi_snapshot_preview1" => create_preview1_instance(store, *config),
@@ -297,7 +297,7 @@ pub unsafe extern "C" fn wasi_instance_new(
})), })),
Err(e) => { Err(e) => {
*trap = Box::into_raw(Box::new(wasm_trap_t { *trap = Box::into_raw(Box::new(wasm_trap_t {
trap: HostRef::new(Trap::new(e)), trap: HostRef::new(store, Trap::new(e)),
})); }));
None None
@@ -335,13 +335,14 @@ pub extern "C" fn wasi_instance_bind_import<'a>(
if &export.ty() != import.ty.func()? { if &export.ty() != import.ty.func()? {
return None; return None;
} }
let store = export.store();
let entry = instance let entry = instance
.export_cache .export_cache
.entry(name.to_string()) .entry(name.to_string())
.or_insert_with(|| { .or_insert_with(|| {
Box::new(wasm_extern_t { Box::new(wasm_extern_t {
which: ExternHost::Func(HostRef::new(export.clone())), which: ExternHost::Func(HostRef::new(store, export.clone())),
}) })
}); });
Some(entry) Some(entry)

View File

@@ -303,7 +303,7 @@ fn compile(env: CompileEnv<'_>) -> Result<ModuleCacheDataTupleType, CompileError
let func_index = env.local.func_index(*i); let func_index = env.local.func_index(*i);
let mut context = Context::new(); let mut context = Context::new();
context.func.name = get_func_name(func_index); context.func.name = get_func_name(func_index);
context.func.signature = env.local.func_signature(func_index).clone(); context.func.signature = env.local.native_func_signature(func_index).clone();
if env.tunables.debug_info { if env.tunables.debug_info {
context.func.collect_debug_info(); context.func.collect_debug_info();
} }

View File

@@ -24,6 +24,6 @@ pub mod wasm {
pub use cranelift_wasm::{ pub use cranelift_wasm::{
get_vmctx_value_label, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, get_vmctx_value_label, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
DefinedTableIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, DefinedTableIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory,
MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex, WasmFuncType, WasmType,
}; };
} }

View File

@@ -549,7 +549,7 @@ impl lightbeam::ModuleContext for FuncEnvironment<'_> {
} }
fn signature(&self, index: u32) -> &Self::Signature { fn signature(&self, index: u32) -> &Self::Signature {
&self.module.signatures[SignatureIndex::from_u32(index)] &self.module.signatures[SignatureIndex::from_u32(index)].1
} }
fn defined_table_index(&self, table_index: u32) -> Option<u32> { fn defined_table_index(&self, table_index: u32) -> Option<u32> {
@@ -658,6 +658,13 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm
fn target_config(&self) -> TargetFrontendConfig { fn target_config(&self) -> TargetFrontendConfig {
self.target_config self.target_config
} }
fn reference_type(&self) -> ir::Type {
// For now, the only reference types we support are `externref`, which
// don't require tracing GC and stack maps. So we just use the target's
// pointer type. This will have to change once we move to tracing GC.
self.pointer_type()
}
} }
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
@@ -915,7 +922,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
func: &mut ir::Function, func: &mut ir::Function,
index: SignatureIndex, index: SignatureIndex,
) -> WasmResult<ir::SigRef> { ) -> WasmResult<ir::SigRef> {
Ok(func.import_signature(self.module.signatures[index].clone())) Ok(func.import_signature(self.module.signatures[index].1.clone()))
} }
fn make_direct_func( fn make_direct_func(
@@ -923,7 +930,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
func: &mut ir::Function, func: &mut ir::Function,
index: FuncIndex, index: FuncIndex,
) -> WasmResult<ir::FuncRef> { ) -> WasmResult<ir::FuncRef> {
let sig = self.module.func_signature(index); let sig = self.module.native_func_signature(index);
let signature = func.import_signature(sig.clone()); let signature = func.import_signature(sig.clone());
let name = get_func_name(index); let name = get_func_name(index);
Ok(func.import_function(ir::ExtFuncData { Ok(func.import_function(ir::ExtFuncData {

View File

@@ -7,7 +7,7 @@ use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{ use cranelift_wasm::{
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table,
TableIndex, TableIndex, WasmFuncType,
}; };
use indexmap::IndexMap; use indexmap::IndexMap;
use more_asserts::assert_ge; use more_asserts::assert_ge;
@@ -181,7 +181,7 @@ pub struct Module {
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct ModuleLocal { pub struct ModuleLocal {
/// Unprocessed signatures exactly as provided by `declare_signature()`. /// Unprocessed signatures exactly as provided by `declare_signature()`.
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>, pub signatures: PrimaryMap<SignatureIndex, (WasmFuncType, ir::Signature)>,
/// Number of imported functions in the module. /// Number of imported functions in the module.
pub num_imported_funcs: usize, pub num_imported_funcs: usize,
@@ -332,8 +332,15 @@ impl ModuleLocal {
index.index() < self.num_imported_globals index.index() < self.num_imported_globals
} }
/// Convenience method for looking up the signature of a function. /// Convenience method for looking up the native signature of a compiled
pub fn func_signature(&self, func_index: FuncIndex) -> &ir::Signature { /// Wasm function.
&self.signatures[self.functions[func_index]] pub fn native_func_signature(&self, func_index: FuncIndex) -> &ir::Signature {
&self.signatures[self.functions[func_index]].1
}
/// Convenience method for looking up the original Wasm signature of a
/// function.
pub fn wasm_func_type(&self, func_index: FuncIndex) -> &WasmFuncType {
&self.signatures[self.functions[func_index]].0
} }
} }

View File

@@ -92,6 +92,13 @@ impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
fn target_config(&self) -> TargetFrontendConfig { fn target_config(&self) -> TargetFrontendConfig {
self.result.target_config self.result.target_config
} }
fn reference_type(&self) -> ir::Type {
// For now, the only reference types we support are `externref`, which
// don't require tracing GC and stack maps. So we just use the target's
// pointer type. This will have to change once we move to tracing GC.
self.pointer_type()
}
} }
/// This trait is useful for `translate_module` because it tells how to translate /// This trait is useful for `translate_module` because it tells how to translate
@@ -106,10 +113,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
Ok(()) Ok(())
} }
fn declare_signature(&mut self, _wasm: &WasmFuncType, sig: ir::Signature) -> WasmResult<()> { fn declare_signature(&mut self, wasm: &WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
let sig = translate_signature(sig, self.pointer_type()); let sig = translate_signature(sig, self.pointer_type());
// TODO: Deduplicate signatures. // TODO: Deduplicate signatures.
self.result.module.local.signatures.push(sig); self.result
.module
.local
.signatures
.push((wasm.clone(), sig));
Ok(()) Ok(())
} }

View File

@@ -149,8 +149,10 @@ impl Compiler {
let mut cx = FunctionBuilderContext::new(); let mut cx = FunctionBuilderContext::new();
let mut trampolines = HashMap::new(); let mut trampolines = HashMap::new();
let mut trampoline_relocations = HashMap::new(); let mut trampoline_relocations = HashMap::new();
for sig in translation.module.local.signatures.values() { for (wasm_func_ty, native_sig) in translation.module.local.signatures.values() {
let index = self.signatures.register(sig); let index = self
.signatures
.register(wasm_func_ty.clone(), native_sig.clone());
if trampolines.contains_key(&index) { if trampolines.contains_key(&index) {
continue; continue;
} }
@@ -158,7 +160,7 @@ impl Compiler {
&*self.isa, &*self.isa,
&mut self.code_memory, &mut self.code_memory,
&mut cx, &mut cx,
sig, native_sig,
std::mem::size_of::<u128>(), std::mem::size_of::<u128>(),
)?; )?;
trampolines.insert(index, trampoline); trampolines.insert(index, trampoline);
@@ -167,7 +169,7 @@ impl Compiler {
// show up be sure to log it in case anyone's listening and there's // show up be sure to log it in case anyone's listening and there's
// an accidental bug. // an accidental bug.
if relocations.len() > 0 { if relocations.len() > 0 {
log::info!("relocations found in trampoline for {:?}", sig); log::info!("relocations found in trampoline for {:?}", native_sig);
trampoline_relocations.insert(index, relocations); trampoline_relocations.insert(index, relocations);
} }
} }

View File

@@ -31,8 +31,8 @@ pub fn resolve_imports(
match (import, &export) { match (import, &export) {
(EntityIndex::Function(func_index), Some(Export::Function(f))) => { (EntityIndex::Function(func_index), Some(Export::Function(f))) => {
let import_signature = module.local.func_signature(*func_index); let import_signature = module.local.native_func_signature(*func_index);
let signature = signatures.lookup(f.signature).unwrap(); let signature = signatures.lookup_native(f.signature).unwrap();
if signature != *import_signature { if signature != *import_signature {
// TODO: If the difference is in the calling convention, // TODO: If the difference is in the calling convention,
// we could emit a wrapper function to fix it up. // we could emit a wrapper function to fix it up.

View File

@@ -93,7 +93,7 @@ impl<'data> RawCompiledModule<'data> {
.local .local
.signatures .signatures
.values() .values()
.map(|sig| signature_registry.register(sig)) .map(|(wasm, native)| signature_registry.register(wasm.clone(), native.clone()))
.collect::<PrimaryMap<_, _>>() .collect::<PrimaryMap<_, _>>()
}; };

View File

@@ -57,6 +57,21 @@
//! to hold everything satisfies its alignment. It also ensures that we don't //! to hold everything satisfies its alignment. It also ensures that we don't
//! need a ton of excess padding between the `VMExternData` and the value for //! need a ton of excess padding between the `VMExternData` and the value for
//! values with large alignment. //! values with large alignment.
//!
//! ## Reference Counting Protocol and Wasm Functions
//!
//! Currently, `VMExternRef`s passed into compiled Wasm functions have move
//! semantics: the host code gives up ownership and does not decrement the
//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm
//! functions also have move semantics: host code takes ownership and the
//! reference count is not incremented.
//!
//! This works well when a reference is passed into Wasm and then passed back
//! out again. However, if a reference is passed into Wasm, but not passed back
//! out, then the reference is leaked. This is only a temporary state, and
//! follow up work will leverage stack maps to fix this issue. Follow
//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on
//! this.
use std::alloc::Layout; use std::alloc::Layout;
use std::any::Any; use std::any::Any;

View File

@@ -2,11 +2,11 @@
//! signature checking. //! signature checking.
use crate::vmcontext::VMSharedSignatureIndex; use crate::vmcontext::VMSharedSignatureIndex;
use more_asserts::{assert_lt, debug_assert_lt}; use more_asserts::assert_lt;
use std::collections::{hash_map, HashMap}; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::RwLock; use std::sync::RwLock;
use wasmtime_environ::ir; use wasmtime_environ::{ir, wasm::WasmFuncType};
/// WebAssembly requires that the caller and callee signatures in an indirect /// WebAssembly requires that the caller and callee signatures in an indirect
/// call must match. To implement this efficiently, keep a registry of all /// call must match. To implement this efficiently, keep a registry of all
@@ -24,8 +24,13 @@ pub struct SignatureRegistry {
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Inner { struct Inner {
signature2index: HashMap<ir::Signature, VMSharedSignatureIndex>, wasm2index: HashMap<WasmFuncType, VMSharedSignatureIndex>,
index2signature: HashMap<VMSharedSignatureIndex, ir::Signature>,
// Maps the index to the original Wasm signature.
index2wasm: HashMap<VMSharedSignatureIndex, WasmFuncType>,
// Maps the index to the native signature.
index2native: HashMap<VMSharedSignatureIndex, ir::Signature>,
} }
impl SignatureRegistry { impl SignatureRegistry {
@@ -37,37 +42,42 @@ impl SignatureRegistry {
} }
/// Register a signature and return its unique index. /// Register a signature and return its unique index.
pub fn register(&self, sig: &ir::Signature) -> VMSharedSignatureIndex { pub fn register(&self, wasm: WasmFuncType, native: ir::Signature) -> VMSharedSignatureIndex {
let mut inner = self.inner.write().unwrap(); let Inner {
let len = inner.signature2index.len(); wasm2index,
match inner.signature2index.entry(sig.clone()) { index2wasm,
hash_map::Entry::Occupied(entry) => *entry.get(), index2native,
hash_map::Entry::Vacant(entry) => { } = &mut *self.inner.write().unwrap();
let len = wasm2index.len();
*wasm2index.entry(wasm.clone()).or_insert_with(|| {
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) // Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
// is reserved for VMSharedSignatureIndex::default(). // is reserved for VMSharedSignatureIndex::default().
debug_assert_lt!( assert_lt!(
len, len,
std::u32::MAX as usize, std::u32::MAX as usize,
"Invariant check: signature_hash.len() < std::u32::MAX" "Invariant check: signature_hash.len() < std::u32::MAX"
); );
let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
entry.insert(sig_id); index2wasm.insert(index, wasm);
inner.index2signature.insert(sig_id, sig.clone()); index2native.insert(index, native);
sig_id index
} })
}
} }
/// Looks up a shared signature index within this registry. /// Looks up a shared native signature within this registry.
/// ///
/// Note that for this operation to be semantically correct the `idx` must /// Note that for this operation to be semantically correct the `idx` must
/// have previously come from a call to `register` of this same object. /// have previously come from a call to `register` of this same object.
pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> { pub fn lookup_native(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> {
self.inner self.inner.read().unwrap().index2native.get(&idx).cloned()
.read() }
.unwrap()
.index2signature /// Looks up a shared Wasm signature within this registry.
.get(&idx) ///
.cloned() /// Note that for this operation to be semantically correct the `idx` must
/// have previously come from a call to `register` of this same object.
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
self.inner.read().unwrap().index2wasm.get(&idx).cloned()
} }
} }

View File

@@ -244,9 +244,10 @@ impl Func {
// number of arguments and the right types of arguments. As a result // number of arguments and the right types of arguments. As a result
// we should be able to safely run through them all and read them. // we should be able to safely run through them all and read them.
let mut args = Vec::with_capacity(ty_clone.params().len()); let mut args = Vec::with_capacity(ty_clone.params().len());
let store = Store::upgrade(&store_weak).unwrap();
for (i, ty) in ty_clone.params().iter().enumerate() { for (i, ty) in ty_clone.params().iter().enumerate() {
unsafe { unsafe {
args.push(Val::read_value_from(values_vec.add(i), ty)); args.push(Val::read_value_from(&store, values_vec.add(i), ty));
} }
} }
let mut returns = vec![Val::null(); ty_clone.results().len()]; let mut returns = vec![Val::null(); ty_clone.results().len()];
@@ -483,25 +484,25 @@ impl Func {
.store .store
.compiler() .compiler()
.signatures() .signatures()
.lookup(self.export.signature) .lookup_wasm(self.export.signature)
.expect("failed to lookup signature"); .expect("failed to lookup signature");
// This is only called with `Export::Function`, and since it's coming // This is only called with `Export::Function`, and since it's coming
// from wasmtime_runtime itself we should support all the types coming // from wasmtime_runtime itself we should support all the types coming
// out of it, so assert such here. // out of it, so assert such here.
FuncType::from_wasmtime_signature(&sig).expect("core wasm signature should be supported") FuncType::from_wasm_func_type(&sig).expect("core wasm signature should be supported")
} }
/// Returns the number of parameters that this function takes. /// Returns the number of parameters that this function takes.
pub fn param_arity(&self) -> usize { pub fn param_arity(&self) -> usize {
let sig = self self.instance
.instance
.store .store
.compiler() .compiler()
.signatures() .signatures()
.lookup(self.export.signature) .lookup_wasm(self.export.signature)
.expect("failed to lookup signature"); .expect("failed to lookup signature")
sig.params.len() - 2 // skip the two vmctx leading parameters .params
.len()
} }
/// Returns the number of results this function produces. /// Returns the number of results this function produces.
@@ -511,7 +512,7 @@ impl Func {
.store .store
.compiler() .compiler()
.signatures() .signatures()
.lookup(self.export.signature) .lookup_wasm(self.export.signature)
.expect("failed to lookup signature"); .expect("failed to lookup signature");
sig.returns.len() sig.returns.len()
} }
@@ -546,7 +547,11 @@ impl Func {
let param_tys = my_ty.params().iter(); let param_tys = my_ty.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) { for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != *ty { if arg.ty() != *ty {
bail!("argument type mismatch"); bail!(
"argument type mismatch: found {} but expected {}",
arg.ty(),
ty
);
} }
if !arg.comes_from_same_store(&self.instance.store) { if !arg.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not currently supported"); bail!("cross-`Store` values are not currently supported");
@@ -571,7 +576,7 @@ impl Func {
for (index, ty) in my_ty.results().iter().enumerate() { for (index, ty) in my_ty.results().iter().enumerate() {
unsafe { unsafe {
let ptr = values_vec.as_ptr().add(index); let ptr = values_vec.as_ptr().add(index);
results.push(Val::read_value_from(ptr, ty)); results.push(Val::read_value_from(&self.instance.store, ptr, ty));
} }
} }
@@ -722,7 +727,10 @@ impl Func {
(get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15) (get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)
} }
pub(crate) fn store(&self) -> &Store { // This should be `pub(crate)` but the C API happens to need it in one
// place.
#[doc(hidden)]
pub fn store(&self) -> &Store {
&self.instance.store &self.instance.store
} }
} }
@@ -1038,6 +1046,12 @@ impl Caller<'_> {
Some(Extern::Memory(mem)) Some(Extern::Memory(mem))
} }
} }
/// Get a handle to this caller's store.
pub fn store(&self) -> Store {
// See comment above the `store` member for why this unwrap is OK.
Store::upgrade(&self.store).unwrap()
}
} }
macro_rules! impl_into_func { macro_rules! impl_into_func {

276
crates/wasmtime/src/ref.rs Normal file → Executable file
View File

@@ -2,224 +2,182 @@
use std::any::Any; use std::any::Any;
use std::cell::{self, RefCell}; use std::cell::{self, RefCell};
use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::marker::PhantomData;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use wasmtime_runtime::VMExternRef;
trait InternalRefBase: Any {
fn as_any(&self) -> &dyn Any;
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;
fn set_host_info(&self, info: Option<Box<dyn Any>>);
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
}
#[derive(Clone)]
pub struct InternalRef(Rc<dyn InternalRefBase>);
impl InternalRef {
pub fn is_ref<T: 'static>(&self) -> bool {
let r = self.0.as_any();
Any::is::<HostRef<T>>(r)
}
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
let r = self.0.as_any();
r.downcast_ref::<HostRef<T>>()
.expect("reference is not T type")
.clone()
}
}
struct AnyAndHostInfo {
any: Box<dyn Any>,
host_info: Option<Box<dyn Any>>,
}
#[derive(Clone)]
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
/// Represents an opaque reference to any data within WebAssembly. /// Represents an opaque reference to any data within WebAssembly.
#[derive(Clone)] #[derive(Clone)]
pub enum ExternRef { pub struct ExternRef {
/// A reference to no data. pub(crate) inner: VMExternRef,
Null, pub(crate) store: Weak<crate::runtime::StoreInner>,
/// A reference to data stored internally in `wasmtime`.
Ref(InternalRef),
/// A reference to data located outside of `wasmtime`.
Other(OtherRef),
} }
impl ExternRef { impl ExternRef {
/// Creates a new instance of `ExternRef` from `Box<dyn Any>`. /// Creates a new instance of `ExternRef` wrapping the given value.
pub fn new(data: Box<dyn Any>) -> Self { pub fn new<T>(store: &crate::Store, value: T) -> ExternRef
let info = AnyAndHostInfo { where
any: data, T: 'static + Any,
host_info: None, {
}; let inner = VMExternRef::new(value);
ExternRef::Other(OtherRef(Rc::new(RefCell::new(info)))) let store = store.weak();
ExternRef { inner, store }
} }
/// Creates a `Null` reference. /// Creates a `Null` reference.
pub fn null() -> Self { pub fn null() -> Option<Self> {
ExternRef::Null None
} }
/// Returns the data stored in the reference if available. /// Get this reference's store.
/// # Panics ///
/// Panics if the variant isn't `ExternRef::Other`. /// Returns `None` if this reference outlived its store.
pub fn data(&self) -> cell::Ref<Box<dyn Any>> { pub fn store(&self) -> Option<crate::runtime::Store> {
match self { crate::runtime::Store::upgrade(&self.store)
ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
_ => panic!("expected ExternRef::Other"),
}
} }
/// Returns true if the two `ExternRef<T>`'s point to the same value (not just /// Get the underlying data for this `ExternRef`.
/// values that compare as equal). pub fn data(&self) -> &dyn Any {
&*self.inner
}
/// Does this `ExternRef` point to the same inner value as `other`?0
///
/// This is *only* pointer equality, and does *not* run any inner value's
/// `Eq` implementation.
pub fn ptr_eq(&self, other: &ExternRef) -> bool { pub fn ptr_eq(&self, other: &ExternRef) -> bool {
match (self, other) { self.inner == other.inner
(ExternRef::Null, ExternRef::Null) => true,
(ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {
a.ptr_eq(b.as_ref())
}
(ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {
Rc::ptr_eq(a, b)
}
_ => false,
}
} }
/// Returns a mutable reference to the host information if available. /// Returns the host information for this `externref`, if previously created
/// # Panics /// with `set_host_info`.
/// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`. pub fn host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> { let store = crate::Store::upgrade(&self.store)?;
match self { store.host_info(self)
ExternRef::Null => panic!("null"),
ExternRef::Ref(r) => r.0.host_info(),
ExternRef::Other(r) => {
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
}
} }
/// Sets the host information for an `ExternRef`. /// Set the host information for this `externref`, returning the old host
/// # Panics /// information if it was previously set.
/// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`. pub fn set_host_info<T>(&self, info: T) -> Option<Rc<RefCell<dyn Any>>>
pub fn set_host_info(&self, info: Option<Box<dyn Any>>) { where
match self { T: 'static + Any,
ExternRef::Null => panic!("null"), {
ExternRef::Ref(r) => r.0.set_host_info(info), let store = crate::Store::upgrade(&self.store)?;
ExternRef::Other(r) => { store.set_host_info(self, Some(Rc::new(RefCell::new(info))))
r.0.borrow_mut().host_info = info;
}
} }
/// Remove the host information for this `externref`, returning the old host
/// information if it was previously set.
pub fn remove_host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {
let store = crate::Store::upgrade(&self.store)?;
store.set_host_info(self, None)
} }
} }
impl fmt::Debug for ExternRef { impl fmt::Debug for ExternRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { let ExternRef { inner, store: _ } = self;
ExternRef::Null => write!(f, "null"), f.debug_struct("ExternRef")
ExternRef::Ref(_) => write!(f, "externref"), .field("inner", &inner)
ExternRef::Other(_) => write!(f, "other ref"), .field("store", &"..")
.finish()
} }
}
}
struct ContentBox<T> {
content: T,
host_info: Option<Box<dyn Any>>,
externref_data: Weak<dyn InternalRefBase>,
} }
/// Represents a piece of data located in the host environment. /// Represents a piece of data located in the host environment.
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>); #[derive(Debug)]
pub struct HostRef<T>
where
T: 'static + Any,
{
externref: ExternRef,
_phantom: PhantomData<T>,
}
impl<T: 'static> HostRef<T> { impl<T> HostRef<T>
where
T: 'static + Any,
{
/// Creates a new `HostRef<T>` from `T`. /// Creates a new `HostRef<T>` from `T`.
pub fn new(item: T) -> HostRef<T> { pub fn new(store: &crate::Store, item: T) -> HostRef<T> {
let externref_data: Weak<HostRef<T>> = Weak::new(); HostRef {
let content = ContentBox { externref: ExternRef::new(store, RefCell::new(item)),
content: item, _phantom: PhantomData,
host_info: None, }
externref_data,
};
HostRef(Rc::new(RefCell::new(content)))
} }
/// Immutably borrows the wrapped data. /// Immutably borrows the wrapped data.
///
/// # Panics /// # Panics
///
/// Panics if the value is currently mutably borrowed. /// Panics if the value is currently mutably borrowed.
pub fn borrow(&self) -> cell::Ref<T> { pub fn borrow(&self) -> cell::Ref<T> {
cell::Ref::map(self.0.borrow(), |b| &b.content) self.inner().borrow()
} }
/// Mutably borrows the wrapped data. /// Mutably borrows the wrapped data.
///
/// # Panics /// # Panics
///
/// Panics if the `HostRef<T>` is already borrowed. /// Panics if the `HostRef<T>` is already borrowed.
pub fn borrow_mut(&self) -> cell::RefMut<T> { pub fn borrow_mut(&self) -> cell::RefMut<T> {
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content) self.inner().borrow_mut()
} }
/// Returns true if the two `HostRef<T>`'s point to the same value (not just /// Returns true if the two `HostRef<T>`'s point to the same value (not just
/// values that compare as equal). /// values that compare as equal).
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool { pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
Rc::ptr_eq(&self.0, &other.0) self.externref.ptr_eq(&other.externref)
} }
/// Returns an opaque reference to the wrapped data in the form of fn inner(&self) -> &RefCell<T> {
/// an `ExternRef`. self.externref
/// # Panics .inner
/// Panics if `HostRef<T>` is already mutably borrowed. .downcast_ref::<RefCell<T>>()
pub fn externref(&self) -> ExternRef { .expect("`HostRef<T>`s always wrap an `ExternRef` of `RefCell<T>`")
let r = self.0.borrow_mut().externref_data.upgrade();
if let Some(r) = r {
return ExternRef::Ref(InternalRef(r));
}
let externref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
self.0.borrow_mut().externref_data = Rc::downgrade(&externref_data);
ExternRef::Ref(InternalRef(externref_data))
} }
} }
impl<T: 'static> InternalRefBase for HostRef<T> { impl<T> AsRef<ExternRef> for HostRef<T> {
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool { fn as_ref(&self) -> &ExternRef {
if let Some(other) = other.as_any().downcast_ref() { &self.externref
self.ptr_eq(other) }
}
impl<T> From<HostRef<T>> for ExternRef
where
T: 'static + Any,
{
fn from(host: HostRef<T>) -> ExternRef {
host.externref
}
}
impl<T> TryFrom<ExternRef> for HostRef<T>
where
T: 'static + Any,
{
type Error = ExternRef;
fn try_from(externref: ExternRef) -> Result<Self, ExternRef> {
if externref.inner.is::<RefCell<T>>() {
Ok(HostRef {
externref,
_phantom: PhantomData,
})
} else { } else {
false Err(externref)
} }
} }
fn as_any(&self) -> &dyn Any {
self
}
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
fn set_host_info(&self, info: Option<Box<dyn Any>>) {
self.0.borrow_mut().host_info = info;
}
} }
impl<T> Clone for HostRef<T> { impl<T> Clone for HostRef<T> {
fn clone(&self) -> HostRef<T> { fn clone(&self) -> HostRef<T> {
HostRef(self.0.clone()) HostRef {
} externref: self.externref.clone(),
} _phantom: PhantomData,
}
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ref(")?;
self.0.borrow().content.fmt(f)?;
write!(f, ")")
} }
} }

View File

@@ -1,8 +1,11 @@
use crate::externals::MemoryCreator; use crate::externals::MemoryCreator;
use crate::r#ref::ExternRef;
use crate::trampoline::{MemoryCreatorProxy, StoreInstanceHandle}; use crate::trampoline::{MemoryCreatorProxy, StoreInstanceHandle};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp; use std::cmp;
use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
@@ -14,7 +17,7 @@ use wasmtime_environ::{CacheConfig, Tunables};
use wasmtime_jit::{native, CompilationStrategy, Compiler}; use wasmtime_jit::{native, CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent}; use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{ use wasmtime_runtime::{
debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMInterrupts, debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMExternRef, VMInterrupts,
}; };
// Runtime Environment // Runtime Environment
@@ -733,6 +736,7 @@ pub(crate) struct StoreInner {
compiler: RefCell<Compiler>, compiler: RefCell<Compiler>,
instances: RefCell<Vec<InstanceHandle>>, instances: RefCell<Vec<InstanceHandle>>,
signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>, signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>,
host_info: RefCell<HashMap<VMExternRef, Rc<RefCell<dyn Any>>>>,
} }
impl Store { impl Store {
@@ -758,6 +762,7 @@ impl Store {
compiler: RefCell::new(compiler), compiler: RefCell::new(compiler),
instances: RefCell::new(Vec::new()), instances: RefCell::new(Vec::new()),
signal_handler: RefCell::new(None), signal_handler: RefCell::new(None),
host_info: RefCell::new(HashMap::new()),
}), }),
} }
} }
@@ -809,6 +814,37 @@ impl Store {
Rc::downgrade(&self.inner) Rc::downgrade(&self.inner)
} }
pub(crate) fn upgrade(weak: &Weak<StoreInner>) -> Option<Self> {
let inner = weak.upgrade()?;
Some(Self { inner })
}
pub(crate) fn host_info(&self, externref: &ExternRef) -> Option<Rc<RefCell<dyn Any>>> {
debug_assert!(
std::rc::Weak::ptr_eq(&self.weak(), &externref.store),
"externref must be from this store"
);
let infos = self.inner.host_info.borrow();
infos.get(&externref.inner).cloned()
}
pub(crate) fn set_host_info(
&self,
externref: &ExternRef,
info: Option<Rc<RefCell<dyn Any>>>,
) -> Option<Rc<RefCell<dyn Any>>> {
debug_assert!(
std::rc::Weak::ptr_eq(&self.weak(), &externref.store),
"externref must be from this store"
);
let mut infos = self.inner.host_info.borrow_mut();
if let Some(info) = info {
infos.insert(externref.inner.clone(), info)
} else {
infos.remove(&externref.inner)
}
}
pub(crate) fn signal_handler(&self) -> std::cell::Ref<'_, Option<Box<SignalHandler<'static>>>> { pub(crate) fn signal_handler(&self) -> std::cell::Ref<'_, Option<Box<SignalHandler<'static>>>> {
self.inner.signal_handler.borrow() self.inner.signal_handler.borrow()
} }

View File

@@ -32,7 +32,12 @@ pub(crate) fn create_handle(
.local .local
.signatures .signatures
.values() .values()
.map(|sig| store.compiler().signatures().register(sig)) .map(|(wasm, native)| {
store
.compiler()
.signatures()
.register(wasm.clone(), native.clone())
})
.collect::<PrimaryMap<_, _>>(); .collect::<PrimaryMap<_, _>>();
unsafe { unsafe {

View File

@@ -225,7 +225,10 @@ pub fn create_handle_with_function(
// First up we manufacture a trampoline which has the ABI specified by `ft` // First up we manufacture a trampoline which has the ABI specified by `ft`
// and calls into `stub_fn`... // and calls into `stub_fn`...
let sig_id = module.local.signatures.push(sig.clone()); let sig_id = module
.local
.signatures
.push((ft.to_wasm_func_type(), sig.clone()));
let func_id = module.local.functions.push(sig_id); let func_id = module.local.functions.push(sig_id);
module module
.exports .exports
@@ -244,7 +247,10 @@ pub fn create_handle_with_function(
mem::size_of::<u128>(), mem::size_of::<u128>(),
)?; )?;
assert!(relocations.is_empty()); assert!(relocations.is_empty());
let sig_id = store.compiler().signatures().register(&sig); let sig_id = store
.compiler()
.signatures()
.register(ft.to_wasm_func_type(), sig);
trampolines.insert(sig_id, trampoline); trampolines.insert(sig_id, trampoline);
// Next up we wrap everything up into an `InstanceHandle` by publishing our // Next up we wrap everything up into an `InstanceHandle` by publishing our
@@ -285,13 +291,19 @@ pub unsafe fn create_handle_with_raw_function(
let mut finished_functions = PrimaryMap::new(); let mut finished_functions = PrimaryMap::new();
let mut trampolines = HashMap::new(); let mut trampolines = HashMap::new();
let sig_id = module.local.signatures.push(sig.clone()); let sig_id = module
.local
.signatures
.push((ft.to_wasm_func_type(), sig.clone()));
let func_id = module.local.functions.push(sig_id); let func_id = module.local.functions.push(sig_id);
module module
.exports .exports
.insert("trampoline".to_string(), EntityIndex::Function(func_id)); .insert("trampoline".to_string(), EntityIndex::Function(func_id));
finished_functions.push(func); finished_functions.push(func);
let sig_id = store.compiler().signatures().register(&sig); let sig_id = store
.compiler()
.signatures()
.register(ft.to_wasm_func_type(), sig);
trampolines.insert(sig_id, trampoline); trampolines.insert(sig_id, trampoline);
create_handle(module, store, finished_functions, trampolines, state) create_handle(module, store, finished_functions, trampolines, state)

View File

@@ -67,6 +67,20 @@ pub enum ValType {
FuncRef, FuncRef,
} }
impl fmt::Display for ValType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ValType::I32 => write!(f, "i32"),
ValType::I64 => write!(f, "i64"),
ValType::F32 => write!(f, "f32"),
ValType::F64 => write!(f, "f64"),
ValType::V128 => write!(f, "v128"),
ValType::ExternRef => write!(f, "externref"),
ValType::FuncRef => write!(f, "funcref"),
}
}
}
impl ValType { impl ValType {
/// Returns true if `ValType` matches any of the numeric types. (e.g. `I32`, /// Returns true if `ValType` matches any of the numeric types. (e.g. `I32`,
/// `I64`, `F32`, `F64`). /// `I64`, `F32`, `F64`).
@@ -106,6 +120,31 @@ impl ValType {
_ => None, _ => None,
} }
} }
pub(crate) fn to_wasm_type(&self) -> wasm::WasmType {
match self {
Self::I32 => wasm::WasmType::I32,
Self::I64 => wasm::WasmType::I64,
Self::F32 => wasm::WasmType::F32,
Self::F64 => wasm::WasmType::F64,
Self::V128 => wasm::WasmType::V128,
Self::FuncRef => wasm::WasmType::FuncRef,
Self::ExternRef => wasm::WasmType::ExternRef,
}
}
pub(crate) fn from_wasm_type(ty: &wasm::WasmType) -> Option<Self> {
match ty {
wasm::WasmType::I32 => Some(Self::I32),
wasm::WasmType::I64 => Some(Self::I64),
wasm::WasmType::F32 => Some(Self::F32),
wasm::WasmType::F64 => Some(Self::F64),
wasm::WasmType::V128 => Some(Self::V128),
wasm::WasmType::FuncRef => Some(Self::FuncRef),
wasm::WasmType::ExternRef => Some(Self::ExternRef),
wasm::WasmType::Func | wasm::WasmType::EmptyBlockType => None,
}
}
} }
// External Types // External Types
@@ -184,12 +223,6 @@ impl From<TableType> for ExternType {
} }
} }
// Function Types
fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
assert_eq!(param.purpose, ir::ArgumentPurpose::Normal);
ValType::from_wasmtime_type(param.value_type)
}
/// A descriptor for a function in a WebAssembly module. /// A descriptor for a function in a WebAssembly module.
/// ///
/// WebAssembly functions can have 0 or more parameters and results. /// WebAssembly functions can have 0 or more parameters and results.
@@ -218,6 +251,13 @@ impl FuncType {
&self.results &self.results
} }
pub(crate) fn to_wasm_func_type(&self) -> wasm::WasmFuncType {
wasm::WasmFuncType {
params: self.params.iter().map(|p| p.to_wasm_type()).collect(),
returns: self.results.iter().map(|r| r.to_wasm_type()).collect(),
}
}
/// Returns `Some` if this function signature was compatible with cranelift, /// Returns `Some` if this function signature was compatible with cranelift,
/// or `None` if one of the types/results wasn't supported or compatible /// or `None` if one of the types/results wasn't supported or compatible
/// with cranelift. /// with cranelift.
@@ -251,17 +291,16 @@ impl FuncType {
/// Returns `None` if any types in the signature can't be converted to the /// Returns `None` if any types in the signature can't be converted to the
/// types in this crate, but that should very rarely happen and largely only /// types in this crate, but that should very rarely happen and largely only
/// indicate a bug in our cranelift integration. /// indicate a bug in our cranelift integration.
pub(crate) fn from_wasmtime_signature(signature: &ir::Signature) -> Option<FuncType> { pub(crate) fn from_wasm_func_type(signature: &wasm::WasmFuncType) -> Option<FuncType> {
let params = signature let params = signature
.params .params
.iter() .iter()
.skip(2) // skip the caller/callee vmctx .map(|p| ValType::from_wasm_type(p))
.map(|p| from_wasmtime_abiparam(p))
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
let results = signature let results = signature
.returns .returns
.iter() .iter()
.map(|p| from_wasmtime_abiparam(p)) .map(|r| ValType::from_wasm_type(r))
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
Some(FuncType { Some(FuncType {
params: params.into_boxed_slice(), params: params.into_boxed_slice(),
@@ -390,7 +429,7 @@ impl MemoryType {
#[derive(Clone, Hash, Eq, PartialEq)] #[derive(Clone, Hash, Eq, PartialEq)]
pub(crate) enum EntityType<'module> { pub(crate) enum EntityType<'module> {
Function(&'module ir::Signature), Function(&'module wasm::WasmFuncType),
Table(&'module wasm::Table), Table(&'module wasm::Table),
Memory(&'module wasm::Memory), Memory(&'module wasm::Memory),
Global(&'module wasm::Global), Global(&'module wasm::Global),
@@ -404,7 +443,7 @@ impl<'module> EntityType<'module> {
) -> EntityType<'module> { ) -> EntityType<'module> {
match entity_index { match entity_index {
EntityIndex::Function(func_index) => { EntityIndex::Function(func_index) => {
let sig = module.local.func_signature(*func_index); let sig = module.local.wasm_func_type(*func_index);
EntityType::Function(&sig) EntityType::Function(&sig)
} }
EntityIndex::Table(table_index) => { EntityIndex::Table(table_index) => {
@@ -422,7 +461,7 @@ impl<'module> EntityType<'module> {
/// Convert this `EntityType` to an `ExternType`. /// Convert this `EntityType` to an `ExternType`.
pub(crate) fn extern_type(&self) -> ExternType { pub(crate) fn extern_type(&self) -> ExternType {
match self { match self {
EntityType::Function(sig) => FuncType::from_wasmtime_signature(sig) EntityType::Function(sig) => FuncType::from_wasm_func_type(sig)
.expect("core wasm function type should be supported") .expect("core wasm function type should be supported")
.into(), .into(),
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(), EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),

View File

@@ -2,6 +2,7 @@ use crate::r#ref::ExternRef;
use crate::{Func, Store, ValType}; use crate::{Func, Store, ValType};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::ptr; use std::ptr;
use wasmtime_runtime::VMExternRef;
/// Possible runtime values that a WebAssembly module can either consume or /// Possible runtime values that a WebAssembly module can either consume or
/// produce. /// produce.
@@ -26,9 +27,7 @@ pub enum Val {
F64(u64), F64(u64),
/// An `externref` value which can hold opaque data to the wasm instance itself. /// An `externref` value which can hold opaque data to the wasm instance itself.
/// ExternRef(Option<ExternRef>),
/// Note that this is a nullable value as well.
ExternRef(ExternRef),
/// A first-class reference to a WebAssembly function. /// A first-class reference to a WebAssembly function.
FuncRef(Func), FuncRef(Func),
@@ -87,18 +86,31 @@ impl Val {
Val::F32(u) => ptr::write(p as *mut u32, *u), Val::F32(u) => ptr::write(p as *mut u32, *u),
Val::F64(u) => ptr::write(p as *mut u64, *u), Val::F64(u) => ptr::write(p as *mut u64, *u),
Val::V128(b) => ptr::write(p as *mut u128, *b), Val::V128(b) => ptr::write(p as *mut u128, *b),
Val::ExternRef(None) => ptr::write(p, 0),
Val::ExternRef(Some(x)) => ptr::write(p as *mut *mut u8, x.inner.clone().into_raw()),
_ => unimplemented!("Val::write_value_to"), _ => unimplemented!("Val::write_value_to"),
} }
} }
pub(crate) unsafe fn read_value_from(p: *const u128, ty: &ValType) -> Val { pub(crate) unsafe fn read_value_from(store: &Store, p: *const u128, ty: &ValType) -> Val {
match ty { match ty {
ValType::I32 => Val::I32(ptr::read(p as *const i32)), ValType::I32 => Val::I32(ptr::read(p as *const i32)),
ValType::I64 => Val::I64(ptr::read(p as *const i64)), ValType::I64 => Val::I64(ptr::read(p as *const i64)),
ValType::F32 => Val::F32(ptr::read(p as *const u32)), ValType::F32 => Val::F32(ptr::read(p as *const u32)),
ValType::F64 => Val::F64(ptr::read(p as *const u64)), ValType::F64 => Val::F64(ptr::read(p as *const u64)),
ValType::V128 => Val::V128(ptr::read(p as *const u128)), ValType::V128 => Val::V128(ptr::read(p as *const u128)),
_ => unimplemented!("Val::read_value_from"), ValType::ExternRef => {
let raw = ptr::read(p as *const *mut u8);
if raw.is_null() {
Val::ExternRef(None)
} else {
Val::ExternRef(Some(ExternRef {
inner: VMExternRef::from_raw(raw),
store: store.weak(),
}))
}
}
_ => unimplemented!("Val::read_value_from: {:?}", ty),
} }
} }
@@ -112,24 +124,31 @@ impl Val {
(V128(u128) v128 unwrap_v128 *e) (V128(u128) v128 unwrap_v128 *e)
} }
/// Attempt to access the underlying value of this `Val`, returning /// Attempt to access the underlying `externref` value of this `Val`.
/// `None` if it is not the correct type.
/// ///
/// This will return `Some` for both the `ExternRef` and `FuncRef` types. /// If this is not an `externref`, then `None` is returned.
pub fn externref(&self) -> Option<ExternRef> { ///
/// If this is a null `externref`, then `Some(None)` is returned.
///
/// If this is a non-null `externref`, then `Some(Some(..))` is returned.
pub fn externref(&self) -> Option<Option<ExternRef>> {
match self { match self {
Val::ExternRef(e) => Some(e.clone()), Val::ExternRef(e) => Some(e.clone()),
_ => None, _ => None,
} }
} }
/// Returns the underlying value of this `Val`, panicking if it's the /// Returns the underlying `externref` value of this `Val`, panicking if it's the
/// wrong type. /// wrong type.
/// ///
/// If this is a null `externref`, then `None` is returned.
///
/// If this is a non-null `externref`, then `Some(..)` is returned.
///
/// # Panics /// # Panics
/// ///
/// Panics if `self` is not of the right type. /// Panics if `self` is not a (nullable) `externref`.
pub fn unwrap_externref(&self) -> ExternRef { pub fn unwrap_externref(&self) -> Option<ExternRef> {
self.externref().expect("expected externref") self.externref().expect("expected externref")
} }
@@ -140,8 +159,8 @@ impl Val {
// TODO: need to implement this once we actually finalize what // TODO: need to implement this once we actually finalize what
// `externref` will look like and it's actually implemented to pass it // `externref` will look like and it's actually implemented to pass it
// to compiled wasm as well. // to compiled wasm as well.
Val::ExternRef(ExternRef::Ref(_)) | Val::ExternRef(ExternRef::Other(_)) => false, Val::ExternRef(Some(e)) => e.store.ptr_eq(&store.weak()),
Val::ExternRef(ExternRef::Null) => true, Val::ExternRef(None) => true,
// Integers have no association with any particular store, so // Integers have no association with any particular store, so
// they're always considered as "yes I came from that store", // they're always considered as "yes I came from that store",
@@ -176,6 +195,12 @@ impl From<f64> for Val {
impl From<ExternRef> for Val { impl From<ExternRef> for Val {
fn from(val: ExternRef) -> Val { fn from(val: ExternRef) -> Val {
Val::ExternRef(Some(val))
}
}
impl From<Option<ExternRef>> for Val {
fn from(val: Option<ExternRef>) -> Val {
Val::ExternRef(val) Val::ExternRef(val)
} }
} }
@@ -194,7 +219,7 @@ pub(crate) fn into_checked_anyfunc(
bail!("cross-`Store` values are not supported"); bail!("cross-`Store` values are not supported");
} }
Ok(match val { Ok(match val {
Val::ExternRef(ExternRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc { Val::ExternRef(None) => wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(), func_ptr: ptr::null(),
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(), type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
vmctx: ptr::null_mut(), vmctx: ptr::null_mut(),
@@ -216,7 +241,7 @@ pub(crate) fn from_checked_anyfunc(
store: &Store, store: &Store,
) -> Val { ) -> Val {
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() { if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
return Val::ExternRef(ExternRef::Null); return Val::ExternRef(None);
} }
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) }; let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
let export = wasmtime_runtime::ExportFunction { let export = wasmtime_runtime::ExportFunction {

View File

@@ -35,7 +35,7 @@ pub fn link_spectest(linker: &mut Linker) -> Result<()> {
linker.define("spectest", "global_f64", g)?; linker.define("spectest", "global_f64", g)?;
let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20))); let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
let table = Table::new(linker.store(), ty, Val::ExternRef(ExternRef::Null))?; let table = Table::new(linker.store(), ty, Val::ExternRef(None))?;
linker.define("spectest", "table", table)?; linker.define("spectest", "table", table)?;
let ty = MemoryType::new(Limits::new(1, Some(2))); let ty = MemoryType::new(Limits::new(1, Some(2)));

View File

@@ -3,11 +3,14 @@ use anyhow::{anyhow, bail, Context as _, Result};
use std::path::Path; use std::path::Path;
use std::str; use std::str;
use wasmtime::*; use wasmtime::*;
use wast::parser::{self, ParseBuffer};
use wast::Wat; use wast::Wat;
use wast::{
parser::{self, ParseBuffer},
RefType,
};
/// Translate from a `script::Value` to a `RuntimeValue`. /// Translate from a `script::Value` to a `RuntimeValue`.
fn runtime_value(v: &wast::Expression<'_>) -> Result<Val> { fn runtime_value(store: &Store, v: &wast::Expression<'_>) -> Result<Val> {
use wast::Instruction::*; use wast::Instruction::*;
if v.instrs.len() != 1 { if v.instrs.len() != 1 {
@@ -19,6 +22,8 @@ fn runtime_value(v: &wast::Expression<'_>) -> Result<Val> {
F32Const(x) => Val::F32(x.bits), F32Const(x) => Val::F32(x.bits),
F64Const(x) => Val::F64(x.bits), F64Const(x) => Val::F64(x.bits),
V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())), V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())),
RefNull(RefType::Extern) => Val::ExternRef(None),
RefExtern(x) => Val::ExternRef(Some(ExternRef::new(store, *x))),
other => bail!("couldn't convert {:?} to a runtime value", other), other => bail!("couldn't convert {:?} to a runtime value", other),
}) })
} }
@@ -114,7 +119,7 @@ impl WastContext {
let values = exec let values = exec
.args .args
.iter() .iter()
.map(runtime_value) .map(|v| runtime_value(&self.store, v))
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
self.invoke(exec.module.map(|i| i.name()), exec.name, &values) self.invoke(exec.module.map(|i| i.name()), exec.name, &values)
} }
@@ -403,6 +408,18 @@ fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result<bool>
(Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b), (Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b),
(Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b), (Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b),
(Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b), (Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b),
(Val::ExternRef(x), wast::AssertExpression::RefNull(wast::RefType::Extern)) => x.is_none(),
(Val::ExternRef(x), wast::AssertExpression::RefExtern(y)) => {
if let Some(x) = x {
let x = x
.data()
.downcast_ref::<u32>()
.expect("only u32 externrefs created in wast test suites");
x == y
} else {
false
}
}
_ => bail!( _ => bail!(
"don't know how to compare {:?} and {:?} yet", "don't know how to compare {:?} and {:?} yet",
actual, actual,

View File

@@ -28,47 +28,27 @@ fn bad_tables() {
// get out of bounds // get out of bounds
let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1))); let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
let t = Table::new( let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
&Store::default(),
ty.clone(),
Val::ExternRef(ExternRef::Null),
)
.unwrap();
assert!(t.get(0).is_none()); assert!(t.get(0).is_none());
assert!(t.get(u32::max_value()).is_none()); assert!(t.get(u32::max_value()).is_none());
// set out of bounds or wrong type // set out of bounds or wrong type
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1))); let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
let t = Table::new( let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
&Store::default(),
ty.clone(),
Val::ExternRef(ExternRef::Null),
)
.unwrap();
assert!(t.set(0, Val::I32(0)).is_err()); assert!(t.set(0, Val::I32(0)).is_err());
assert!(t.set(0, Val::ExternRef(ExternRef::Null)).is_ok()); assert!(t.set(0, Val::ExternRef(None)).is_ok());
assert!(t.set(1, Val::ExternRef(ExternRef::Null)).is_err()); assert!(t.set(1, Val::ExternRef(None)).is_err());
// grow beyond max // grow beyond max
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1))); let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
let t = Table::new( let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
&Store::default(), assert!(t.grow(0, Val::ExternRef(None)).is_ok());
ty.clone(), assert!(t.grow(1, Val::ExternRef(None)).is_err());
Val::ExternRef(ExternRef::Null),
)
.unwrap();
assert!(t.grow(0, Val::ExternRef(ExternRef::Null)).is_ok());
assert!(t.grow(1, Val::ExternRef(ExternRef::Null)).is_err());
assert_eq!(t.size(), 1); assert_eq!(t.size(), 1);
// grow wrong type // grow wrong type
let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2))); let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2)));
let t = Table::new( let t = Table::new(&Store::default(), ty.clone(), Val::ExternRef(None)).unwrap();
&Store::default(),
ty.clone(),
Val::ExternRef(ExternRef::Null),
)
.unwrap();
assert!(t.grow(1, Val::I32(0)).is_err()); assert!(t.grow(1, Val::I32(0)).is_err());
assert_eq!(t.size(), 1); assert_eq!(t.size(), 1);
} }
@@ -88,7 +68,7 @@ fn cross_store() -> anyhow::Result<()> {
let ty = MemoryType::new(Limits::new(1, None)); let ty = MemoryType::new(Limits::new(1, None));
let memory = Memory::new(&store2, ty); let memory = Memory::new(&store2, ty);
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store2, ty, Val::ExternRef(ExternRef::Null))?; let table = Table::new(&store2, ty, Val::ExternRef(None))?;
let need_func = Module::new(&store1, r#"(module (import "" "" (func)))"#)?; let need_func = Module::new(&store1, r#"(module (import "" "" (func)))"#)?;
assert!(Instance::new(&need_func, &[func.into()]).is_err()); assert!(Instance::new(&need_func, &[func.into()]).is_err());

View File

@@ -56,11 +56,11 @@ fn link_twice_bad() -> Result<()> {
// tables // tables
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?; let table = Table::new(&store, ty, Val::ExternRef(None))?;
linker.define("", "", table.clone())?; linker.define("", "", table.clone())?;
assert!(linker.define("", "", table.clone()).is_err()); assert!(linker.define("", "", table.clone()).is_err());
let ty = TableType::new(ValType::FuncRef, Limits::new(2, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?; let table = Table::new(&store, ty, Val::ExternRef(None))?;
assert!(linker.define("", "", table.clone()).is_err()); assert!(linker.define("", "", table.clone()).is_err());
Ok(()) Ok(())
} }

View File

@@ -4,9 +4,9 @@ use wasmtime::*;
fn get_none() { fn get_none() {
let store = Store::default(); let store = Store::default();
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null)).unwrap(); let table = Table::new(&store, ty, Val::ExternRef(None)).unwrap();
match table.get(0) { match table.get(0) {
Some(Val::ExternRef(ExternRef::Null)) => {} Some(Val::ExternRef(None)) => {}
_ => panic!(), _ => panic!(),
} }
assert!(table.get(1).is_none()); assert!(table.get(1).is_none());

View File

@@ -341,7 +341,7 @@ fn mismatched_arguments() -> Result<()> {
); );
assert_eq!( assert_eq!(
func.call(&[Val::F32(0)]).unwrap_err().to_string(), func.call(&[Val::F32(0)]).unwrap_err().to_string(),
"argument type mismatch", "argument type mismatch: found f32 but expected i32",
); );
assert_eq!( assert_eq!(
func.call(&[Val::I32(0), Val::I32(1)]) func.call(&[Val::I32(0), Val::I32(1)])

View File

@@ -11,7 +11,7 @@ fn use_func_after_drop() -> Result<()> {
assert_eq!(closed_over_data, "abcd"); assert_eq!(closed_over_data, "abcd");
}); });
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?; table = Table::new(&store, ty, Val::ExternRef(None))?;
table.set(0, func.into())?; table.set(0, func.into())?;
} }
let func = table.get(0).unwrap().funcref().unwrap().clone(); let func = table.get(0).unwrap().funcref().unwrap().clone();

View File

@@ -0,0 +1,8 @@
(module
(func (export "identity") (param externref) (result externref)
local.get 0))
(assert_return (invoke "identity" (ref.null extern))
(ref.null extern))
(assert_return (invoke "identity" (ref.extern 1))
(ref.extern 1))