//! Memory management for tables. //! //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; use crate::Trap; use std::cell::RefCell; use std::convert::{TryFrom, TryInto}; use wasmtime_environ::wasm::TableElementType; use wasmtime_environ::{ir, TablePlan, TableStyle}; /// A table instance. #[derive(Debug)] pub struct Table { vec: RefCell>, maximum: Option, } impl Table { /// Create a new table instance with specified minimum and maximum number of elements. pub fn new(plan: &TablePlan) -> Self { match plan.table.ty { TableElementType::Func => (), TableElementType::Val(ty) => { unimplemented!("tables of types other than anyfunc ({})", ty) } }; match plan.style { TableStyle::CallerChecksSignature => Self { vec: RefCell::new(vec![ VMCallerCheckedAnyfunc::default(); usize::try_from(plan.table.minimum).unwrap() ]), maximum: plan.table.maximum, }, } } /// Returns the number of allocated elements. pub fn size(&self) -> u32 { self.vec.borrow().len().try_into().unwrap() } /// 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(&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.borrow_mut().resize( usize::try_from(new_len).unwrap(), 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 { self.vec.borrow().get(index as usize).cloned() } /// Set reference to the specified element. /// /// # Panics /// /// Panics if `index` is out of bounds. pub fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), ()> { match self.vec.borrow_mut().get_mut(index as usize) { Some(slot) => { *slot = func; Ok(()) } None => Err(()), } } /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. /// /// # Errors /// /// Returns an error if the range is out of bounds of either the source or /// destination tables. pub fn copy( dst_table: &Self, src_table: &Self, dst_index: u32, src_index: u32, len: u32, source_loc: ir::SourceLoc, ) -> Result<(), Trap> { // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy if src_index .checked_add(len) .map_or(true, |n| n > src_table.size()) || dst_index .checked_add(len) .map_or(true, |m| m > dst_table.size()) { return Err(Trap::wasm(source_loc, ir::TrapCode::TableOutOfBounds)); } let srcs = src_index..src_index + len; let dsts = dst_index..dst_index + len; // Note on the unwraps: the bounds check above means that these will // never panic. // // TODO(#983): investigate replacing this get/set loop with a `memcpy`. if dst_index <= src_index { for (s, d) in (srcs).zip(dsts) { dst_table.set(d, src_table.get(s).unwrap()).unwrap(); } } else { for (s, d) in srcs.rev().zip(dsts.rev()) { dst_table.set(d, src_table.get(s).unwrap()).unwrap(); } } Ok(()) } /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. pub fn vmtable(&self) -> VMTableDefinition { let mut vec = self.vec.borrow_mut(); VMTableDefinition { base: vec.as_mut_ptr() as *mut u8, current_elements: vec.len().try_into().unwrap(), } } }