use crate::error::*;
use crate::value::*;
pub const DEFAULT_STACK_LIMIT: usize = 1024 * 1024;
pub const INIT_STACK_SIZE: usize = 1024;
#[derive(Debug)]
pub struct StackPtr(pub usize);
#[derive(Debug, Clone, Copy)]
pub struct StackValue(pub u64);
#[derive(Debug, Default, Clone, Copy)]
pub struct Frame {
pub bp: usize,
pub sbp: usize,
}
#[derive(Debug, Clone)]
pub struct Stack {
stack: Vec<StackValue>,
frame: Frame,
limit: usize,
}
impl Stack {
pub fn new() -> Stack {
Self::new_with_limit(DEFAULT_STACK_LIMIT)
}
pub fn new_with_limit(limit: usize) -> Stack {
Stack {
stack: Vec::with_capacity(INIT_STACK_SIZE),
frame: Default::default(),
limit,
}
}
#[inline]
pub fn len(&self) -> usize {
self.stack.len()
}
pub fn frame_size(&self) -> usize {
self.len() - self.frame.sbp
}
fn check_overflow(&mut self, need: usize) -> Trap<usize> {
let len = self.len();
let space = self.limit - len;
if space < need {
eprintln!("StackOverflow: limit={}, len={}", self.limit, len);
return Err(TrapKind::StackOverflow);
}
Ok(len)
}
pub fn push_frame(&mut self, params: usize, locals: usize) -> Trap<Frame> {
let cur_size = self.frame_size();
if cur_size < params {
eprintln!("StackOverflow: frame size={}, needed params={}", cur_size, params);
return Err(TrapKind::StackOverflow);
}
let old_frame = self.frame;
let bp = self.len() - params;
self.frame = Frame{
bp,
sbp: bp + locals,
};
Ok(old_frame)
}
pub fn reserve_locals(&mut self, locals: usize) -> Trap<()> {
if locals > 0 {
let new_len = self.len()
.checked_add(locals)
.ok_or(TrapKind::StackOverflow)?;
self.stack.truncate(new_len);
}
Ok(())
}
pub fn pop_frame(&mut self, old_frame: Frame) {
self.stack.truncate(self.frame.bp);
self.frame = old_frame;
}
pub fn push_params(&mut self, params: &[Value]) -> Trap<usize> {
let len = self.check_overflow(params.len())?;
for val in params.iter() {
self.stack.push(StackValue::from(*val));
}
Ok(len)
}
pub fn drop_values(&mut self, count: u32) -> Trap<()> {
let len = self.len();
let new_len = len
.checked_sub(count as usize)
.ok_or(TrapKind::StackOverflow)?;
self.stack.truncate(new_len);
Ok(())
}
#[inline]
pub fn tee_local(&mut self, local: LocalIdx) -> Trap<()> {
let val = self.top_val()?;
let idx = self.frame.bp + local as usize;
self.stack[idx] = val;
Ok(())
}
#[inline]
pub fn set_local(&mut self, local: LocalIdx) -> Trap<()> {
let val = self.pop_val()?;
let idx = self.frame.bp + local as usize;
self.stack[idx] = val;
Ok(())
}
#[inline]
pub fn get_local(&mut self, local: LocalIdx) -> Trap<()> {
let idx = self.frame.bp + local as usize;
self.push_val(self.stack[idx])
}
#[inline]
pub fn set_local_val(&mut self, local: LocalIdx, val: StackValue) {
let idx = self.frame.bp + local as usize;
self.stack[idx] = val;
}
#[inline]
pub fn get_local_val(&mut self, local: LocalIdx) -> StackValue {
let idx = self.frame.bp + local as usize;
self.stack[idx]
}
#[inline]
pub fn push_val(&mut self, val: StackValue) -> Trap<()> {
if self.len() >= self.limit {
eprintln!("StackOverflow: limit={}, len={}", self.limit, self.len());
return Err(TrapKind::StackOverflow);
}
self.stack.push(val);
Ok(())
}
pub fn pop_typed(&mut self, val_type: ValueType) -> Trap<Value> {
match val_type {
ValueType::I32 => self.pop().map(Value::I32),
ValueType::I64 => self.pop().map(Value::I64),
ValueType::F32 => self.pop().map(Value::F32),
ValueType::F64 => self.pop().map(Value::F64),
}
}
#[inline]
pub fn pop_val(&mut self) -> Trap<StackValue> {
self.stack.pop().ok_or(TrapKind::StackOverflow)
}
#[inline]
pub fn top_val(&mut self) -> Trap<StackValue> {
self.stack.last().map(|x| *x)
.ok_or(TrapKind::StackOverflow)
}
#[inline]
pub fn unop<F>(&mut self, op: F) -> Trap<()>
where F: FnOnce(&mut StackValue) -> Trap<()>
{
let mut val = self.stack.last_mut()
.ok_or(TrapKind::StackOverflow)?;
op(&mut val)
}
#[inline]
pub fn binop<F>(&mut self, op: F) -> Trap<()>
where F: FnOnce(&mut StackValue, StackValue) -> Trap<()>
{
let right = self.pop_val()?;
let mut left = self.stack.last_mut()
.ok_or(TrapKind::StackOverflow)?;
op(&mut left, right)
}
}
impl Default for Stack {
fn default() -> Stack {
Self::new()
}
}
pub trait FromValue
where
Self: Sized,
{
fn from_value(val: StackValue) -> Self;
}
pub trait FromStack<T>: Sized {
fn push(&mut self, val: T) -> Trap<()>;
fn pop(&mut self) -> Trap<T>;
fn pop_pair(&mut self) -> Trap<(T, T)>;
}
macro_rules! impl_stack_value {
($($t:ty),*) => {
$(
impl FromValue for $t {
fn from_value(StackValue(val): StackValue) -> Self {
val as _
}
}
impl From<$t> for StackValue {
fn from(other: $t) -> StackValue {
StackValue(other as _)
}
}
impl FromStack<$t> for Stack {
#[inline]
fn push(&mut self, val: $t) -> Trap<()> {
self.push_val(StackValue(val as _))
}
#[inline]
fn pop(&mut self) -> Trap<$t> {
self.pop_val().map(|x| x.0 as _)
}
fn pop_pair(&mut self) -> Trap<($t, $t)> {
let right = self.pop()?;
let left = self.pop()?;
Ok((left, right))
}
}
)*
};
}
impl_stack_value!(i8, u8, i16, u16, i32, u32, i64, u64);
macro_rules! impl_stack_value_float {
($($t:ty),*) => {
$(
impl FromValue for $t {
fn from_value(StackValue(val): StackValue) -> Self {
<$t>::from_bits(val as _)
}
}
impl From<$t> for StackValue {
fn from(other: $t) -> Self {
StackValue(other.to_bits() as _)
}
}
impl FromStack<$t> for Stack {
#[inline]
fn push(&mut self, val: $t) -> Trap<()> {
self.push_val(StackValue(val.to_bits() as _))
}
#[inline]
fn pop(&mut self) -> Trap<$t> {
self.pop_val().map(|x| <$t>::from_bits(x.0 as _))
}
fn pop_pair(&mut self) -> Trap<($t, $t)> {
let right = self.pop()?;
let left = self.pop()?;
Ok((left, right))
}
}
)*
};
}
impl_stack_value_float!(f32, f64);
impl From<Value> for StackValue {
fn from(val: Value) -> StackValue {
match val {
Value::I32(v) => StackValue(v as _),
Value::I64(v) => StackValue(v as _),
Value::F32(v) => StackValue(v.to_bits() as _),
Value::F64(v) => StackValue(v.to_bits() as _),
}
}
}