mod frames;
mod values;
pub use self::{
frames::{CallStack, FuncFrame},
values::{ValueStack, ValueStackRef},
};
use super::{
code_map::{CodeMap, InstructionPtr},
func_types::FuncTypeRegistry,
FuncParams,
};
use crate::{
core::UntypedValue,
func::{HostFuncEntity, WasmFuncEntity},
AsContext,
AsContextMut,
Instance,
};
use core::{
fmt::{self, Display},
mem::size_of,
};
use wasmi_core::{Trap, TrapCode};
const DEFAULT_MIN_VALUE_STACK_HEIGHT: usize = 1024;
const DEFAULT_MAX_VALUE_STACK_HEIGHT: usize = 1024 * DEFAULT_MIN_VALUE_STACK_HEIGHT;
const DEFAULT_MAX_RECURSION_DEPTH: usize = 1024;
#[cold]
fn err_stack_overflow() -> TrapCode {
TrapCode::StackOverflow
}
#[derive(Debug, Copy, Clone)]
pub struct StackLimits {
initial_value_stack_height: usize,
maximum_value_stack_height: usize,
maximum_recursion_depth: usize,
}
#[derive(Debug)]
pub enum LimitsError {
InitialValueStackExceedsMaximum,
}
impl Display for LimitsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LimitsError::InitialValueStackExceedsMaximum => {
write!(f, "initial value stack heihgt exceeds maximum stack height")
}
}
}
}
impl StackLimits {
pub fn new(
initial_value_stack_height: usize,
maximum_value_stack_height: usize,
maximum_recursion_depth: usize,
) -> Result<Self, LimitsError> {
if initial_value_stack_height > maximum_value_stack_height {
return Err(LimitsError::InitialValueStackExceedsMaximum);
}
Ok(Self {
initial_value_stack_height,
maximum_value_stack_height,
maximum_recursion_depth,
})
}
}
impl Default for StackLimits {
fn default() -> Self {
let register_len = size_of::<UntypedValue>();
let initial_value_stack_height = DEFAULT_MIN_VALUE_STACK_HEIGHT / register_len;
let maximum_value_stack_height = DEFAULT_MAX_VALUE_STACK_HEIGHT / register_len;
Self {
initial_value_stack_height,
maximum_value_stack_height,
maximum_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
}
}
}
#[derive(Debug, Default)]
pub struct Stack {
pub(crate) values: ValueStack,
frames: CallStack,
}
impl Stack {
pub fn new(limits: StackLimits) -> Self {
let frames = CallStack::new(limits.maximum_recursion_depth);
let values = ValueStack::new(
limits.initial_value_stack_height,
limits.maximum_value_stack_height,
);
Self { values, frames }
}
pub(crate) fn empty() -> Self {
Self {
values: ValueStack::empty(),
frames: CallStack::default(),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub(super) fn push_frame(&mut self, frame: FuncFrame) -> Result<(), TrapCode> {
self.frames.push(frame)
}
pub(super) fn pop_frame(&mut self) -> Option<FuncFrame> {
self.frames.pop()
}
pub(crate) fn call_wasm_root(
&mut self,
wasm_func: &WasmFuncEntity,
code_map: &CodeMap,
) -> Result<FuncFrame, TrapCode> {
let iref = self.call_wasm_impl(wasm_func, code_map)?;
let instance = wasm_func.instance();
Ok(self.frames.init(iref, instance))
}
pub(crate) fn call_wasm(
&mut self,
caller: &FuncFrame,
wasm_func: &WasmFuncEntity,
code_map: &CodeMap,
) -> Result<FuncFrame, TrapCode> {
let ip = self.call_wasm_impl(wasm_func, code_map)?;
self.frames.push(*caller)?;
let instance = wasm_func.instance();
let frame = FuncFrame::new(ip, instance);
Ok(frame)
}
pub(crate) fn call_wasm_impl(
&mut self,
wasm_func: &WasmFuncEntity,
code_map: &CodeMap,
) -> Result<InstructionPtr, TrapCode> {
let header = code_map.header(wasm_func.func_body());
let max_stack_height = header.max_stack_height();
self.values.reserve(max_stack_height)?;
let len_locals = header.len_locals();
self.values
.extend_zeros(len_locals)
.expect("stack overflow is unexpected due to previous stack reserve");
let iref = header.iref();
let ip = code_map.instr_ptr(iref);
Ok(ip)
}
pub fn return_wasm(&mut self) -> Option<FuncFrame> {
self.frames.pop()
}
pub(crate) fn call_host_root<C>(
&mut self,
ctx: C,
host_func: HostFuncEntity<<C as AsContext>::UserState>,
func_types: &FuncTypeRegistry,
) -> Result<(), Trap>
where
C: AsContextMut,
{
self.call_host_impl(ctx, host_func, None, func_types)
}
pub(crate) fn call_host<C>(
&mut self,
ctx: C,
caller: &FuncFrame,
host_func: HostFuncEntity<<C as AsContext>::UserState>,
func_types: &FuncTypeRegistry,
) -> Result<(), Trap>
where
C: AsContextMut,
{
let instance = caller.instance();
self.call_host_impl(ctx, host_func, Some(instance), func_types)
}
#[inline(never)]
fn call_host_impl<C>(
&mut self,
mut ctx: C,
host_func: HostFuncEntity<<C as AsContext>::UserState>,
instance: Option<&Instance>,
func_types: &FuncTypeRegistry,
) -> Result<(), Trap>
where
C: AsContextMut,
{
let (input_types, output_types) = func_types
.resolve_func_type(host_func.ty_dedup())
.params_results();
let len_inputs = input_types.len();
let len_outputs = output_types.len();
let max_inout = len_inputs.max(len_outputs);
self.values.reserve(max_inout)?;
if len_outputs > len_inputs {
let delta = len_outputs - len_inputs;
self.values.extend_zeros(delta)?;
}
let params_results = FuncParams::new(
self.values.peek_as_slice_mut(max_inout),
len_inputs,
len_outputs,
);
host_func.call(&mut ctx, instance, params_results)?;
if len_outputs < len_inputs {
let delta = len_inputs - len_outputs;
self.values.drop(delta);
}
Ok(())
}
pub fn clear(&mut self) {
self.values.clear();
self.frames.clear();
}
}