tinywasm 0.11.0

A tiny WebAssembly interpreter
Documentation
use crate::{Result, Trap};

use alloc::vec::Vec;
use tinywasm_types::{FuncAddr, ValueCounts};

#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct CallStack {
    stack: Vec<CallFrame>,
    max_size: usize,
    dynamic: bool,
}

impl CallStack {
    pub(crate) fn new(config: &crate::engine::Config) -> Self {
        let stack = config.call_stack;
        Self { stack: Vec::with_capacity(stack.initial_size), max_size: stack.max_size, dynamic: stack.dynamic }
    }

    pub(crate) fn clear(&mut self) {
        self.stack.clear();
    }

    #[inline(always)]
    pub(crate) fn len(&self) -> u32 {
        self.stack.len() as u32
    }

    pub(crate) fn truncate_to(&mut self, len: u32) {
        debug_assert!(len as usize <= self.stack.len());
        self.stack.truncate(len as usize);
    }

    #[inline(always)]
    pub(crate) fn pop_frame(&mut self, base: u32) -> Option<CallFrame> {
        if self.len() == base { None } else { self.stack.pop() }
    }

    #[inline(always)]
    pub(crate) fn push(&mut self, mut call_frame: CallFrame, instr_ptr: usize) -> Result<(), Trap> {
        // Check the limit only at capacity to avoid an extra hot-path check. Vec growth may
        // intentionally overshoot max_size. Revisit when Vec::push_within_capacity is stable.
        if self.stack.len() == self.stack.capacity() && (!self.dynamic || self.stack.len() >= self.max_size) {
            return cold!(Err(Trap::CallStackOverflow));
        }

        call_frame.instr_ptr = instr_ptr;
        self.stack.push(call_frame);
        Ok(())
    }
}

#[derive(Clone, Copy)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct CallFrame {
    pub(crate) instr_ptr: usize,
    pub(crate) func_addr: FuncAddr,
    pub(crate) locals_base: StackBase,
    pub(crate) stack_offset: ValueCounts,
}

#[derive(Clone, Copy, Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct StackBase {
    pub(crate) s32: u32,
    pub(crate) s64: u32,
    pub(crate) s128: u32,
}

impl CallFrame {
    pub(crate) fn new(func_addr: FuncAddr, locals_base: StackBase, stack_offset: ValueCounts) -> Self {
        Self { instr_ptr: 0, func_addr, locals_base, stack_offset }
    }

    #[inline]
    pub(crate) fn stack_base(&self) -> StackBase {
        StackBase {
            s32: self.locals_base.s32 + self.stack_offset.c32 as u32,
            s64: self.locals_base.s64 + self.stack_offset.c64 as u32,
            s128: self.locals_base.s128 + self.stack_offset.c128 as u32,
        }
    }
}