use super::{err_stack_overflow, BaseValueStackOffset, FrameValueStackOffset};
use crate::{
collections::HeadVec,
engine::executor::InstructionPtr,
ir::SlotSpan,
Instance,
TrapCode,
};
use alloc::vec::Vec;
#[cfg(doc)]
use crate::{
engine::executor::stack::ValueStack,
engine::EngineFunc,
ir::Op,
ir::Slot,
Global,
Memory,
Table,
};
#[derive(Debug, Default)]
pub struct CallStack {
frames: Vec<CallFrame>,
instances: HeadVec<Instance>,
recursion_limit: usize,
}
impl CallStack {
pub fn new(recursion_limit: usize) -> Self {
Self {
frames: Vec::new(),
instances: HeadVec::default(),
recursion_limit,
}
}
#[inline(always)]
pub fn reset(&mut self) {
self.frames.clear();
self.instances.clear();
}
#[inline(always)]
fn len(&self) -> usize {
self.frames.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline(always)]
pub fn instance(&self) -> Option<&Instance> {
self.instances.last()
}
#[inline(always)]
#[track_caller]
pub fn instance_expect(&self) -> &Instance {
self.instance()
.expect("the currently used instance must be present")
}
#[inline(always)]
pub fn push(
&mut self,
mut call: CallFrame,
instance: Option<Instance>,
) -> Result<(), TrapCode> {
if self.len() == self.recursion_limit {
return Err(err_stack_overflow());
}
if let Some(instance) = instance {
call.changed_instance = self.push_instance(instance);
}
self.frames.push(call);
Ok(())
}
#[inline(always)]
fn push_instance(&mut self, instance: Instance) -> bool {
if let Some(last) = self.instances.last() {
if instance.eq(last) {
return false;
}
}
self.instances.push(instance);
true
}
#[inline(always)]
pub fn pop(&mut self) -> Option<(CallFrame, Option<Instance>)> {
let frame = self.frames.pop()?;
let instance = match frame.changed_instance {
true => self.instances.pop(),
false => None,
};
Some((frame, instance))
}
#[inline(always)]
pub fn peek(&self) -> Option<&CallFrame> {
self.frames.last()
}
#[inline(always)]
pub fn peek_mut(&mut self) -> Option<&mut CallFrame> {
self.frames.last_mut()
}
pub fn peek_2(&self) -> Option<(&CallFrame, Option<&CallFrame>)> {
let (callee, remaining) = self.frames.split_last()?;
let caller = remaining.last();
Some((callee, caller))
}
}
#[derive(Debug, Copy, Clone)]
pub struct StackOffsets {
pub base: BaseValueStackOffset,
pub frame: FrameValueStackOffset,
}
impl StackOffsets {
#[inline(always)]
fn move_down(&mut self, delta: usize) {
let base = usize::from(self.base);
let frame = usize::from(self.frame);
debug_assert!(delta <= base);
debug_assert!(delta <= frame);
self.base = BaseValueStackOffset::new(base - delta);
self.frame = FrameValueStackOffset::new(frame - delta);
}
}
#[derive(Debug, Copy, Clone)]
pub struct CallFrame {
instr_ptr: InstructionPtr,
offsets: StackOffsets,
results: SlotSpan,
changed_instance: bool,
}
impl CallFrame {
pub fn new(instr_ptr: InstructionPtr, offsets: StackOffsets, results: SlotSpan) -> Self {
Self {
instr_ptr,
offsets,
results,
changed_instance: false,
}
}
pub fn move_down(&mut self, delta: usize) {
self.offsets.move_down(delta);
}
pub fn update_instr_ptr(&mut self, new_instr_ptr: InstructionPtr) {
self.instr_ptr = new_instr_ptr;
}
pub fn instr_ptr(&self) -> InstructionPtr {
self.instr_ptr
}
pub fn frame_offset(&self) -> FrameValueStackOffset {
self.offsets.frame
}
pub fn base_offset(&self) -> BaseValueStackOffset {
self.offsets.base
}
pub fn results(&self) -> SlotSpan {
self.results
}
}