use super::{Executor, InstructionPtr};
use crate::{
core::CoreTable,
engine::{utils::unreachable_unchecked, ResumableOutOfFuelError},
errors::TableError,
ir::{
index::{Elem, Table},
Const32,
Op,
Slot,
},
store::{PrunedStore, StoreError, StoreInner},
Error,
TrapCode,
};
impl Executor<'_> {
fn fetch_table_index(&self, offset: usize) -> Table {
let mut addr: InstructionPtr = self.ip;
addr.add(offset);
match *addr.get() {
Op::TableIndex { index } => index,
unexpected => {
unsafe {
unreachable_unchecked!("expected `Op::TableIndex` but found: {unexpected:?}")
}
}
}
}
fn fetch_element_segment_index(&self, offset: usize) -> Elem {
let mut addr: InstructionPtr = self.ip;
addr.add(offset);
match *addr.get() {
Op::ElemIndex { index } => index,
unexpected => {
unsafe {
unreachable_unchecked!("expected `Op::ElemIndex` but found: {unexpected:?}")
}
}
}
}
pub fn execute_table_get(
&mut self,
store: &StoreInner,
result: Slot,
index: Slot,
) -> Result<(), Error> {
let index: u64 = self.get_stack_slot_as(index);
self.execute_table_get_impl(store, result, index)
}
pub fn execute_table_get_imm(
&mut self,
store: &StoreInner,
result: Slot,
index: Const32<u64>,
) -> Result<(), Error> {
let index: u64 = index.into();
self.execute_table_get_impl(store, result, index)
}
fn execute_table_get_impl(
&mut self,
store: &StoreInner,
result: Slot,
index: u64,
) -> Result<(), Error> {
let table_index = self.fetch_table_index(1);
let table = self.get_table(table_index);
let value = store
.resolve_table(&table)
.get_untyped(index)
.ok_or(TrapCode::TableOutOfBounds)?;
self.set_stack_slot(result, value);
self.try_next_instr_at(2)
}
pub fn execute_table_size(&mut self, store: &StoreInner, result: Slot, table_index: Table) {
self.execute_table_size_impl(store, result, table_index);
self.next_instr();
}
fn execute_table_size_impl(&mut self, store: &StoreInner, result: Slot, table_index: Table) {
let table = self.get_table(table_index);
let size = store.resolve_table(&table).size();
self.set_stack_slot(result, size);
}
pub fn execute_table_set(
&mut self,
store: &mut StoreInner,
index: Slot,
value: Slot,
) -> Result<(), Error> {
let index: u64 = self.get_stack_slot_as(index);
self.execute_table_set_impl(store, index, value)
}
pub fn execute_table_set_at(
&mut self,
store: &mut StoreInner,
index: Const32<u64>,
value: Slot,
) -> Result<(), Error> {
let index: u64 = index.into();
self.execute_table_set_impl(store, index, value)
}
fn execute_table_set_impl(
&mut self,
store: &mut StoreInner,
index: u64,
value: Slot,
) -> Result<(), Error> {
let table_index = self.fetch_table_index(1);
let table = self.get_table(table_index);
let value = self.get_stack_slot(value);
store
.resolve_table_mut(&table)
.set_untyped(index, value)
.map_err(|_| TrapCode::TableOutOfBounds)?;
self.try_next_instr_at(2)
}
pub fn execute_table_copy(
&mut self,
store: &mut StoreInner,
dst: Slot,
src: Slot,
len: Slot,
) -> Result<(), Error> {
let dst: u64 = self.get_stack_slot_as(dst);
let src: u64 = self.get_stack_slot_as(src);
let len: u64 = self.get_stack_slot_as(len);
let dst_table_index = self.fetch_table_index(1);
let src_table_index = self.fetch_table_index(2);
if dst_table_index == src_table_index {
let table = self.get_table(dst_table_index);
let (table, fuel) = store.resolve_table_and_fuel_mut(&table);
table.copy_within(dst, src, len, Some(fuel))?;
} else {
let dst_table = self.get_table(dst_table_index);
let src_table = self.get_table(src_table_index);
let (dst_table, src_table, fuel) =
store.resolve_table_pair_and_fuel(&dst_table, &src_table);
CoreTable::copy(dst_table, dst, src_table, src, len, Some(fuel))?;
}
self.try_next_instr_at(3)
}
pub fn execute_table_init(
&mut self,
store: &mut StoreInner,
dst: Slot,
src: Slot,
len: Slot,
) -> Result<(), Error> {
let dst: u64 = self.get_stack_slot_as(dst);
let src: u32 = self.get_stack_slot_as(src);
let len: u32 = self.get_stack_slot_as(len);
let table_index = self.fetch_table_index(1);
let element_index = self.fetch_element_segment_index(2);
let (table, element, fuel) = store.resolve_table_init_params(
&self.get_table(table_index),
&self.get_element_segment(element_index),
);
table.init(element.as_ref(), dst, src, len, Some(fuel))?;
self.try_next_instr_at(3)
}
pub fn execute_table_fill(
&mut self,
store: &mut StoreInner,
dst: Slot,
len: Slot,
value: Slot,
) -> Result<(), Error> {
let dst: u64 = self.get_stack_slot_as(dst);
let len: u64 = self.get_stack_slot_as(len);
let table_index = self.fetch_table_index(1);
let value = self.get_stack_slot(value);
let table = self.get_table(table_index);
let (table, fuel) = store.resolve_table_and_fuel_mut(&table);
table.fill_untyped(dst, value, len, Some(fuel))?;
self.try_next_instr_at(2)
}
pub fn execute_table_grow(
&mut self,
store: &mut PrunedStore,
result: Slot,
delta: Slot,
value: Slot,
) -> Result<(), Error> {
let delta: u64 = self.get_stack_slot_as(delta);
let table_index = self.fetch_table_index(1);
if delta == 0 {
self.execute_table_size_impl(store.inner(), result, table_index);
return self.try_next_instr_at(2);
}
let table = self.get_table(table_index);
let value = self.get_stack_slot(value);
let return_value = match store.grow_table(&table, delta, value) {
Ok(return_value) => return_value,
Err(StoreError::External(
TableError::GrowOutOfBounds | TableError::OutOfSystemMemory,
)) => {
let table_ty = store.inner().resolve_table(&table).ty();
match table_ty.is_64() {
true => u64::MAX,
false => u64::from(u32::MAX),
}
}
Err(StoreError::External(TableError::OutOfFuel { required_fuel })) => {
return Err(Error::from(ResumableOutOfFuelError::new(required_fuel)))
}
Err(StoreError::External(TableError::ResourceLimiterDeniedAllocation)) => {
return Err(Error::from(TrapCode::GrowthOperationLimited))
}
Err(error) => {
panic!("`memory.grow`: internal interpreter error: {error}")
}
};
self.set_stack_slot(result, return_value);
self.try_next_instr_at(2)
}
pub fn execute_element_drop(&mut self, store: &mut StoreInner, segment_index: Elem) {
let segment = self.get_element_segment(segment_index);
store.resolve_element_mut(&segment).drop_items();
self.next_instr();
}
}