#![allow(clippy::indexing_slicing)]
use super::{cell, Execution};
use crate::error::{ir_get, ir_get_mut};
use crate::instance::Instance;
use crate::module::op::{Op, Slot};
use crate::store::StoreInner;
use crate::trap::Trap;
use crate::value::Val;
use crate::{Error, Result};
fn oob() -> Error {
Trap::TableOutOfBounds.into()
}
impl Execution {
pub(super) fn exec_table(
&mut self,
inner: &mut StoreInner,
op: &Op,
instance: Instance,
base: u32,
) -> Result<()> {
let at = |s: Slot| super::step::slot(base, s);
match op {
Op::TableInit { elem, table, s } => {
self.table_init(inner, instance, (*elem, *table), at(*s))
}
Op::TableCopy {
dst_table,
src_table,
s,
} => self.table_copy(inner, instance, (*dst_table, *src_table), at(*s)),
Op::ElemDrop(elem) => {
*ir_get_mut(&mut inner.instance_mut(instance).elems, *elem)? = Vec::new();
Ok(())
}
Op::TableGet(t, s) => self.table_get(inner, instance, *t, at(*s)),
Op::TableSet(t, s) => self.table_set(inner, instance, *t, at(*s)),
Op::TableSize(t, s) => {
let handle = *ir_get(&inner.instance(instance).tables, *t)?;
let size = inner.table(handle).size();
self.put_index(at(*s), inner.table(handle).ty.is_64(), size)
}
Op::TableFill(t, s) => self.table_fill(inner, instance, *t, at(*s)),
_ => Err(Error::msg(format!("not a table op: {op:?}"))),
}
}
fn table_get(
&mut self,
inner: &StoreInner,
instance: Instance,
table: u32,
at: usize,
) -> Result<()> {
let handle = *ir_get(&inner.instance(instance).tables, table)?;
let idx = self.index_at(at, inner.table(handle).ty.is_64())?;
let r = inner.table(handle).get(idx).ok_or_else(oob)?;
self.set_val(at, Val::from_ref(r))
}
fn table_set(
&mut self,
inner: &mut StoreInner,
instance: Instance,
table: u32,
at: usize,
) -> Result<()> {
let handle = *ir_get(&inner.instance(instance).tables, table)?;
let tt = &inner.table(handle).ty;
let (is_64, kind) = (tt.is_64(), cell::refkind_of_heap(tt.element().heap_type()));
let idx = self.index_at(at, is_64)?;
let val = self.take_ref(at + 1, kind)?.to_ref();
if inner.table_mut(handle).set(idx, val) {
Ok(())
} else {
Err(oob())
}
}
fn table_fill(
&mut self,
inner: &mut StoreInner,
instance: Instance,
table: u32,
at: usize,
) -> Result<()> {
let handle = *ir_get(&inner.instance(instance).tables, table)?;
let tt = &inner.table(handle).ty;
let (is_64, kind) = (tt.is_64(), cell::refkind_of_heap(tt.element().heap_type()));
let dst = self.index_at(at, is_64)?;
let val = self.take_ref(at + 1, kind)?.to_ref();
let len = self.index_at(at + 2, is_64)?;
if inner.table_mut(handle).fill(dst, val, len) {
Ok(())
} else {
Err(oob())
}
}
fn table_init(
&mut self,
inner: &mut StoreInner,
instance: Instance,
(elem, table): (u32, u32),
at: usize,
) -> Result<()> {
let handle = *ir_get(&inner.instance(instance).tables, table)?;
let dst = self.index_at(at, inner.table(handle).ty.is_64())?;
let src = u64::from(self.scalar_at(at + 1)?.unwrap_i32() as u32);
let len = u64::from(self.scalar_at(at + 2)?.unwrap_i32() as u32);
let entity = inner.instance(instance);
let refs = ir_get(&entity.elems, elem)?.clone();
let src_end = checked_range(src, len, refs.len() as u64)?;
checked_range(dst, len, inner.table(handle).size())?;
let (dst, src, src_end) = (dst as usize, src as usize, src_end as usize);
for (i, r) in refs[src..src_end].iter().enumerate() {
inner.table_mut(handle).set((dst + i) as u64, r.clone());
}
Ok(())
}
fn table_copy(
&mut self,
inner: &mut StoreInner,
instance: Instance,
(dst_table, src_table): (u32, u32),
at: usize,
) -> Result<()> {
let entity = inner.instance(instance);
let dst_handle = *ir_get(&entity.tables, dst_table)?;
let src_handle = *ir_get(&entity.tables, src_table)?;
let (dst_64, src_64) = (
inner.table(dst_handle).ty.is_64(),
inner.table(src_handle).ty.is_64(),
);
let dst = self.index_at(at, dst_64)?;
let src = self.index_at(at + 1, src_64)?;
let len = self.index_at(at + 2, dst_64 && src_64)?;
let src_end = checked_range(src, len, inner.table(src_handle).size())?;
checked_range(dst, len, inner.table(dst_handle).size())?;
let (dst, src, src_end) = (dst as usize, src as usize, src_end as usize);
let snapshot = inner.table(src_handle).elems[src..src_end].to_vec();
for (i, r) in snapshot.into_iter().enumerate() {
inner.table_mut(dst_handle).set((dst + i) as u64, r);
}
Ok(())
}
}
fn checked_range(start: u64, len: u64, total: u64) -> Result<u64> {
match start.checked_add(len) {
Some(end) if end <= total => Ok(end),
_ => Err(oob()),
}
}