use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use crate::{Trap, VMExternRef};
use std::cell::RefCell;
use std::convert::{TryFrom, TryInto};
use std::ptr;
use wasmtime_environ::wasm::TableElementType;
use wasmtime_environ::{ir, TablePlan, TableStyle};
#[derive(Debug)]
pub struct Table {
elements: RefCell<TableElements>,
maximum: Option<u32>,
}
#[derive(Clone, Debug)]
pub enum TableElement {
FuncRef(*mut VMCallerCheckedAnyfunc),
ExternRef(Option<VMExternRef>),
}
#[derive(Debug)]
enum TableElements {
FuncRefs(Vec<*mut VMCallerCheckedAnyfunc>),
ExternRefs(Vec<Option<VMExternRef>>),
}
impl Table {
pub fn new(plan: &TablePlan) -> Self {
let min = usize::try_from(plan.table.minimum).unwrap();
let elements = RefCell::new(match plan.table.ty {
TableElementType::Func => TableElements::FuncRefs(vec![ptr::null_mut(); min]),
TableElementType::Val(ty) => {
debug_assert_eq!(ty, crate::ref_type());
TableElements::ExternRefs(vec![None; min])
}
});
match plan.style {
TableStyle::CallerChecksSignature => Self {
elements,
maximum: plan.table.maximum,
},
}
}
pub fn element_type(&self) -> TableElementType {
match &*self.elements.borrow() {
TableElements::FuncRefs(_) => TableElementType::Func,
TableElements::ExternRefs(_) => TableElementType::Val(crate::ref_type()),
}
}
pub fn size(&self) -> u32 {
match &*self.elements.borrow() {
TableElements::FuncRefs(x) => x.len().try_into().unwrap(),
TableElements::ExternRefs(x) => x.len().try_into().unwrap(),
}
}
pub fn fill(&self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> {
let start = dst;
let end = start
.checked_add(len)
.ok_or_else(|| Trap::wasm(ir::TrapCode::TableOutOfBounds))?;
if end > self.size() {
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
}
for i in start..end {
self.set(i, val.clone()).unwrap();
}
Ok(())
}
pub unsafe fn grow(&self, delta: u32, init_value: TableElement) -> Option<u32> {
let size = self.size();
let new_len = size.checked_add(delta)?;
if let Some(max) = self.maximum {
if new_len > max {
return None;
}
}
let new_len = usize::try_from(new_len).unwrap();
match &mut *self.elements.borrow_mut() {
TableElements::FuncRefs(x) => {
let init_value = init_value.try_into().ok()?;
x.resize(new_len, init_value)
}
TableElements::ExternRefs(x) => {
let init_value = init_value.try_into().ok()?;
x.resize(new_len, init_value)
}
}
Some(size)
}
pub fn get(&self, index: u32) -> Option<TableElement> {
match &*self.elements.borrow() {
TableElements::FuncRefs(x) => x.get(index as usize).cloned().map(TableElement::FuncRef),
TableElements::ExternRefs(x) => {
x.get(index as usize).cloned().map(TableElement::ExternRef)
}
}
}
pub fn set(&self, index: u32, elem: TableElement) -> Result<(), ()> {
let mut elems = self.elements.borrow_mut();
match &mut *elems {
TableElements::FuncRefs(x) => {
let slot = x.get_mut(index as usize).ok_or(())?;
*slot = elem.try_into().or(Err(()))?;
}
TableElements::ExternRefs(x) => {
let slot = x.get_mut(index as usize).ok_or(())?;
*slot = elem.try_into().or(Err(()))?;
}
}
Ok(())
}
pub fn copy(
dst_table: &Self,
src_table: &Self,
dst_index: u32,
src_index: u32,
len: u32,
) -> Result<(), Trap> {
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(ir::TrapCode::TableOutOfBounds));
}
let srcs = src_index..src_index + len;
let dsts = dst_index..dst_index + len;
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(())
}
pub fn vmtable(&self) -> VMTableDefinition {
match &*self.elements.borrow() {
TableElements::FuncRefs(x) => VMTableDefinition {
base: x.as_ptr() as *const u8 as *mut u8,
current_elements: x.len().try_into().unwrap(),
},
TableElements::ExternRefs(x) => VMTableDefinition {
base: x.as_ptr() as *const u8 as *mut u8,
current_elements: x.len().try_into().unwrap(),
},
}
}
}
impl TryFrom<TableElement> for *mut VMCallerCheckedAnyfunc {
type Error = TableElement;
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
match e {
TableElement::FuncRef(f) => Ok(f),
_ => Err(e),
}
}
}
impl TryFrom<TableElement> for Option<VMExternRef> {
type Error = TableElement;
fn try_from(e: TableElement) -> Result<Self, Self::Error> {
match e {
TableElement::ExternRef(x) => Ok(x),
_ => Err(e),
}
}
}
impl From<*mut VMCallerCheckedAnyfunc> for TableElement {
fn from(f: *mut VMCallerCheckedAnyfunc) -> TableElement {
TableElement::FuncRef(f)
}
}
impl From<Option<VMExternRef>> for TableElement {
fn from(x: Option<VMExternRef>) -> TableElement {
TableElement::ExternRef(x)
}
}
impl From<VMExternRef> for TableElement {
fn from(x: VMExternRef) -> TableElement {
TableElement::ExternRef(Some(x))
}
}