diff --git a/wasmtime-api/src/externals.rs b/wasmtime-api/src/externals.rs index 46775afbb6..1bfb9cac5e 100644 --- a/wasmtime-api/src/externals.rs +++ b/wasmtime-api/src/externals.rs @@ -1,10 +1,9 @@ use crate::callable::{Callable, NativeCallable, WasmtimeFn, WrappedCallable}; use crate::runtime::Store; -use crate::table_utils; use crate::trampoline::{generate_global_export, generate_memory_export, generate_table_export}; use crate::trap::Trap; use crate::types::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType}; -use crate::values::Val; +use crate::values::{from_checked_anyfunc, into_checked_anyfunc, AnyRef, Val}; use std::cell::RefCell; use std::rc::Rc; use std::result::Result; @@ -244,20 +243,62 @@ impl Global { pub struct Table { store: Rc>, r#type: TableType, - #[allow(dead_code)] wasmtime_handle: InstanceHandle, wasmtime_export: wasmtime_runtime::Export, } +fn get_table_item( + handle: &InstanceHandle, + store: &Rc>, + table_index: cranelift_wasm::DefinedTableIndex, + item_index: u32, +) -> Val { + if let Some(item) = handle.table_get(table_index, item_index) { + from_checked_anyfunc(item, store) + } else { + AnyRef::null().into() + } +} + +fn set_table_item( + handle: &mut InstanceHandle, + store: &Rc>, + table_index: cranelift_wasm::DefinedTableIndex, + item_index: u32, + val: Val, +) -> bool { + let item = into_checked_anyfunc(val, store); + if let Some(item_ref) = handle.table_get_mut(table_index, item_index) { + *item_ref = item; + true + } else { + false + } +} + impl Table { - pub fn new(store: Rc>, r#type: TableType, _init: Val) -> Table { + pub fn new(store: Rc>, r#type: TableType, init: Val) -> Table { match r#type.element() { ValType::FuncRef => (), _ => panic!("table is not for funcref"), } - // TODO implement _init initialization - let (wasmtime_handle, wasmtime_export) = + let (mut wasmtime_handle, wasmtime_export) = generate_table_export(&r#type).expect("generated table"); + + // Initialize entries with the init value. + match wasmtime_export { + wasmtime_runtime::Export::Table { definition, .. } => { + let index = wasmtime_handle.table_index(unsafe { &*definition }); + let len = unsafe { (*definition).current_elements as u32 }; + for i in 0..len { + let _success = + set_table_item(&mut wasmtime_handle, &store, index, i, init.clone()); + assert!(_success); + } + } + _ => panic!("global definition not found"), + } + Table { store, r#type, @@ -270,31 +311,49 @@ impl Table { &self.r#type } - fn wasmtime_table_definition(&self) -> *mut wasmtime_runtime::VMTableDefinition { + fn wasmtime_table_index(&self) -> cranelift_wasm::DefinedTableIndex { match self.wasmtime_export { - wasmtime_runtime::Export::Table { definition, .. } => definition, + wasmtime_runtime::Export::Table { definition, .. } => { + self.wasmtime_handle.table_index(unsafe { &*definition }) + } _ => panic!("global definition not found"), } } pub fn get(&self, index: u32) -> Val { - let definition = self.wasmtime_table_definition(); - unsafe { table_utils::get_item(definition, &self.store, index) } + let table_index = self.wasmtime_table_index(); + get_table_item(&self.wasmtime_handle, &self.store, table_index, index) } pub fn set(&self, index: u32, val: Val) -> bool { - let definition = self.wasmtime_table_definition(); - unsafe { table_utils::set_item(definition, &self.store, index, val) } + let table_index = self.wasmtime_table_index(); + let mut wasmtime_handle = self.wasmtime_handle.clone(); + set_table_item(&mut wasmtime_handle, &self.store, table_index, index, val) } pub fn size(&self) -> u32 { - let definition = self.wasmtime_table_definition(); - unsafe { table_utils::get_size(definition) } + match self.wasmtime_export { + wasmtime_runtime::Export::Table { definition, .. } => unsafe { + (*definition).current_elements as u32 + }, + _ => panic!("global definition not found"), + } } pub fn grow(&mut self, delta: u32, init: Val) -> bool { - let definition = self.wasmtime_table_definition(); - unsafe { table_utils::grow_table(definition, &self.r#type, &self.store, delta, init) } + let index = self.wasmtime_table_index(); + if let Some(len) = self.wasmtime_handle.table_grow(index, delta) { + let mut wasmtime_handle = self.wasmtime_handle.clone(); + for i in 0..delta { + let i = len as u32 - (delta - i); + let _success = + set_table_item(&mut wasmtime_handle, &self.store, index, i, init.clone()); + assert!(_success); + } + true + } else { + false + } } pub(crate) fn wasmtime_export(&self) -> &wasmtime_runtime::Export { diff --git a/wasmtime-api/src/lib.rs b/wasmtime-api/src/lib.rs index e6e4aa1e41..5427a48082 100644 --- a/wasmtime-api/src/lib.rs +++ b/wasmtime-api/src/lib.rs @@ -6,7 +6,6 @@ mod externals; mod instance; mod module; mod runtime; -mod table_utils; mod trampoline; mod trap; mod types; diff --git a/wasmtime-api/src/table_utils.rs b/wasmtime-api/src/table_utils.rs deleted file mode 100644 index 1b67028db4..0000000000 --- a/wasmtime-api/src/table_utils.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::cell::RefCell; -use std::mem; -use std::ptr; -use std::rc::Rc; -use std::slice; -use wasmtime_runtime::{ - InstanceHandle, VMCallerCheckedAnyfunc, VMSharedSignatureIndex, VMTableDefinition, -}; - -use crate::callable::WasmtimeFn; -use crate::runtime::SignatureRegistry; -use crate::runtime::Store; -use crate::types::TableType; -use crate::values::{AnyRef, FuncRef, Val}; - -fn into_checked_anyfunc(val: Val, store: &Rc>) -> VMCallerCheckedAnyfunc { - match val { - Val::AnyRef(AnyRef::Null) => VMCallerCheckedAnyfunc { - func_ptr: ptr::null(), - type_index: VMSharedSignatureIndex::default(), - vmctx: ptr::null_mut(), - }, - Val::AnyRef(AnyRef::Func(f)) | Val::FuncRef(f) => { - let (vmctx, func_ptr, signature) = match f.0.wasmtime_export() { - wasmtime_runtime::Export::Function { - vmctx, - address, - signature, - } => (*vmctx, *address, signature), - _ => panic!("expected function export"), - }; - let type_index = store.borrow_mut().register_cranelift_signature(signature); - VMCallerCheckedAnyfunc { - func_ptr, - type_index, - vmctx, - } - } - _ => panic!("val is not funcref"), - } -} - -unsafe fn from_checked_anyfunc(item: &VMCallerCheckedAnyfunc, store: &Rc>) -> Val { - if item.type_index == VMSharedSignatureIndex::default() { - return Val::AnyRef(AnyRef::Null); - } - let signature = store - .borrow() - .lookup_cranelift_signature(item.type_index) - .expect("signature") - .clone(); - let instance_handle = InstanceHandle::from_vmctx(item.vmctx); - let export = wasmtime_runtime::Export::Function { - address: item.func_ptr, - signature, - vmctx: item.vmctx, - }; - let f = WasmtimeFn::new(store.clone(), instance_handle, export); - Val::FuncRef(FuncRef(Rc::new(f))) -} - -pub unsafe fn get_item( - table: *mut VMTableDefinition, - store: &Rc>, - index: u32, -) -> Val { - let base = slice::from_raw_parts( - (*table).base as *const VMCallerCheckedAnyfunc, - (*table).current_elements, - ); - - from_checked_anyfunc(&base[index as usize], store) -} - -pub unsafe fn set_item( - table: *mut VMTableDefinition, - store: &Rc>, - index: u32, - val: Val, -) -> bool { - let base = slice::from_raw_parts_mut( - (*table).base as *mut VMCallerCheckedAnyfunc, - (*table).current_elements, - ); - if index as usize >= base.len() { - return false; - } - - base[index as usize] = into_checked_anyfunc(val, store); - true -} - -pub unsafe fn get_size(table: *mut VMTableDefinition) -> u32 { - (*table).current_elements as u32 -} - -pub unsafe fn grow_table( - table: *mut VMTableDefinition, - table_type: &TableType, - store: &Rc>, - delta: u32, - init: Val, -) -> bool { - let new_len = (*table).current_elements + delta as usize; - if (table_type.limits().max() as usize) < new_len { - return false; - } - - let mut buffer = Vec::from_raw_parts( - (*table).base as *mut VMCallerCheckedAnyfunc, - (*table).current_elements, - (*table).current_elements, - ); - buffer.resize(new_len, into_checked_anyfunc(init, store)); - buffer.shrink_to_fit(); - assert!(buffer.capacity() == new_len); - - (*table).base = buffer.as_mut_ptr() as *mut u8; - (*table).current_elements = new_len; - mem::forget(buffer); - - true -} diff --git a/wasmtime-api/src/values.rs b/wasmtime-api/src/values.rs index a9a4f9c77f..677963a85d 100644 --- a/wasmtime-api/src/values.rs +++ b/wasmtime-api/src/values.rs @@ -1,6 +1,8 @@ -use crate::callable::WrappedCallable; +use crate::callable::{WasmtimeFn, WrappedCallable}; +use crate::runtime::{SignatureRegistry, Store}; use crate::types::ValType; use std::any::Any; +use std::cell::RefCell; use std::fmt; use std::ptr; use std::rc::Rc; @@ -199,3 +201,55 @@ impl Into for Val { } } } + +pub(crate) fn into_checked_anyfunc( + val: Val, + store: &Rc>, +) -> wasmtime_runtime::VMCallerCheckedAnyfunc { + match val { + Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc { + func_ptr: ptr::null(), + type_index: wasmtime_runtime::VMSharedSignatureIndex::default(), + vmctx: ptr::null_mut(), + }, + Val::AnyRef(AnyRef::Func(f)) | Val::FuncRef(f) => { + let (vmctx, func_ptr, signature) = match f.0.wasmtime_export() { + wasmtime_runtime::Export::Function { + vmctx, + address, + signature, + } => (*vmctx, *address, signature), + _ => panic!("expected function export"), + }; + let type_index = store.borrow_mut().register_cranelift_signature(signature); + wasmtime_runtime::VMCallerCheckedAnyfunc { + func_ptr, + type_index, + vmctx, + } + } + _ => panic!("val is not funcref"), + } +} + +pub(crate) fn from_checked_anyfunc( + item: &wasmtime_runtime::VMCallerCheckedAnyfunc, + store: &Rc>, +) -> Val { + if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() { + return Val::AnyRef(AnyRef::Null); + } + let signature = store + .borrow() + .lookup_cranelift_signature(item.type_index) + .expect("signature") + .clone(); + let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) }; + let export = wasmtime_runtime::Export::Function { + address: item.func_ptr, + signature, + vmctx: item.vmctx, + }; + let f = WasmtimeFn::new(store.clone(), instance_handle, export); + Val::FuncRef(FuncRef(Rc::new(f))) +} diff --git a/wasmtime-runtime/src/instance.rs b/wasmtime-runtime/src/instance.rs index 08131d7b8b..c6c92590b3 100644 --- a/wasmtime-runtime/src/instance.rs +++ b/wasmtime-runtime/src/instance.rs @@ -622,6 +622,47 @@ impl Instance { } None } + + /// Grow table by the specified amount of elements. + /// + /// Returns `None` if table can't be grown by the specified amount + /// of elements. + pub(crate) fn table_grow(&mut self, table_index: DefinedTableIndex, delta: u32) -> Option { + let result = self + .tables + .get_mut(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .grow(delta); + + // Keep current the VMContext pointers used by compiled wasm code. + *self.table_mut(table_index) = self.tables[table_index].vmtable(); + + result + } + + // Get table element by index. + pub(crate) fn table_get( + &self, + table_index: DefinedTableIndex, + index: u32, + ) -> Option<&VMCallerCheckedAnyfunc> { + self.tables + .get(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .get(index) + } + + // Get table mutable element by index. + pub(crate) fn table_get_mut( + &mut self, + table_index: DefinedTableIndex, + index: u32, + ) -> Option<&mut VMCallerCheckedAnyfunc> { + self.tables + .get_mut(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .get_mut(index) + } } /// A handle holding an `Instance` of a WebAssembly module. @@ -871,6 +912,41 @@ impl InstanceHandle { pub fn memory_grow(&mut self, memory_index: DefinedMemoryIndex, delta: u32) -> Option { self.instance_mut().memory_grow(memory_index, delta) } + + /// Return the table index for the given `VMTableDefinition` in this instance. + pub fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex { + self.instance().table_index(table) + } + + /// Grow table in this instance by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub fn table_grow(&mut self, table_index: DefinedTableIndex, delta: u32) -> Option { + self.instance_mut().table_grow(table_index, delta) + } + + /// Get table element reference. + /// + /// Returns `None` if index is out of bounds. + pub fn table_get( + &self, + table_index: DefinedTableIndex, + index: u32, + ) -> Option<&VMCallerCheckedAnyfunc> { + self.instance().table_get(table_index, index) + } + + /// Get mutable table element reference. + /// + /// Returns `None` if index is out of bounds. + pub fn table_get_mut( + &mut self, + table_index: DefinedTableIndex, + index: u32, + ) -> Option<&mut VMCallerCheckedAnyfunc> { + self.instance_mut().table_get_mut(table_index, index) + } } impl InstanceHandle { diff --git a/wasmtime-runtime/src/table.rs b/wasmtime-runtime/src/table.rs index f9dcf125d6..98e38b234f 100644 --- a/wasmtime-runtime/src/table.rs +++ b/wasmtime-runtime/src/table.rs @@ -32,6 +32,48 @@ impl Table { } } + /// Returns the number of allocated elements. + pub fn size(&self) -> u32 { + self.vec.len() as u32 + } + + /// Grow table by the specified amount of elements. + /// + /// Returns `None` if table can't be grown by the specified amount + /// of elements. + pub fn grow(&mut self, delta: u32) -> Option { + let new_len = match self.size().checked_add(delta) { + Some(len) => { + if let Some(max) = self.maximum { + if len > max { + return None; + } + } + len + } + None => { + return None; + } + }; + self.vec + .resize(new_len as usize, VMCallerCheckedAnyfunc::default()); + Some(new_len) + } + + /// Get reference to the specified element. + /// + /// Returns `None` if the index is out of bounds. + pub fn get(&self, index: u32) -> Option<&VMCallerCheckedAnyfunc> { + self.vec.get(index as usize) + } + + /// Get mutable reference to the specified element. + /// + /// Returns `None` if the index is out of bounds. + pub fn get_mut(&mut self, index: u32) -> Option<&mut VMCallerCheckedAnyfunc> { + self.vec.get_mut(index as usize) + } + /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. pub fn vmtable(&mut self) -> VMTableDefinition { VMTableDefinition {