use crate::prelude::*;
use crate::{StackAddress, StackOffset};
use crate::bytecode::HeapRef;
#[derive(Debug)]
pub struct Stack {
data : Vec<u8>,
pub(crate) fp : StackAddress,
base_fp : StackAddress,
}
impl Stack {
pub fn new() -> Self {
Stack {
data : Vec::with_capacity(4096),
fp : 0,
base_fp : 0,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
pub fn sp(self: &Self) -> StackAddress {
self.data.len() as StackAddress
}
#[cfg_attr(not(debug_assertions), inline(always))]
pub fn fp(self: &Self) -> StackAddress {
self.fp
}
pub fn begin(self: &mut Self) {
self.base_fp = self.data.len() as StackAddress;
self.fp = self.base_fp;
}
pub fn reset(self: &mut Self) {
self.fp = self.base_fp;
self.data.truncate(self.base_fp as usize);
}
#[cfg_attr(not(debug_assertions), inline(always))]
pub fn truncate(self: &mut Self, size: StackAddress) {
debug_assert!(size >= self.base_fp);
self.data.truncate(size as usize);
}
pub fn frame(self: &Self) -> &[u8] {
&self.data[self.fp as usize..]
}
pub fn data(self: &Self) -> &[u8] {
&self.data[..]
}
pub fn copy(self: &mut Self, from: StackAddress, to: StackAddress, num_bytes: StackAddress) {
let from = from as usize;
let to = to as usize;
let num_bytes = num_bytes as usize;
self.data.copy_within(from..from+num_bytes, to);
}
pub fn extend(self: &mut Self, from: StackAddress, num_bytes: StackAddress) { debug_assert!(from + num_bytes <= self.data.len() as StackAddress);
let from = from as usize;
let num_bytes = num_bytes as usize;
let mut data: Vec<_> = self.data[from..from+num_bytes].into();
self.data.append(&mut data);
}
pub fn extend_from(self: &mut Self, slice: &[u8]) {
self.data.extend_from_slice(slice);
}
pub fn extend_zero(self: &mut Self, num_bytes: StackAddress) {
self.data.resize(self.data.len() + num_bytes as usize, 0);
}
}
impl Index<StackAddress> for Stack {
type Output = u8;
#[cfg_attr(not(debug_assertions), inline)]
fn index(&self, index: StackAddress) -> &Self::Output {
Index::index(&*self.data, index as usize)
}
}
impl IndexMut<StackAddress> for Stack {
#[cfg_attr(not(debug_assertions), inline)]
fn index_mut(&mut self, index: StackAddress) -> &mut Self::Output {
IndexMut::index_mut(&mut *self.data, index as usize)
}
}
pub trait StackOp<T> {
fn push(self: &mut Self, value: T);
fn pop(self: &mut Self) -> T;
fn store(self: &mut Self, pos: StackAddress, value: T);
fn load(self: &Self, pos: StackAddress) -> T;
fn top(self: &Self) -> T;
}
pub trait StackOffsetOp<T> {
fn offset_fp(self: &Self, offset: StackOffset) -> StackAddress;
fn offset_sp(self: &Self, offset: StackOffset) -> StackAddress;
}
pub trait StackRelativeOp<T>: StackOffsetOp<T> + StackOp<T> {
fn store_fp(self: &mut Self, offset: StackOffset, value: T) {
let pos = self.offset_fp(offset);
self.store(pos, value);
}
fn load_fp(self: &Self, offset: StackOffset) -> T {
let pos = self.offset_fp(offset);
self.load(pos)
}
fn store_sp(self: &mut Self, offset: StackOffset, value: T) {
let pos = self.offset_sp(offset);
self.store(pos, value);
}
fn load_sp(self: &Self, offset: StackOffset) -> T {
let pos = self.offset_sp(offset);
self.load(pos)
}
}
impl<T> StackOffsetOp<T> for Stack {
#[cfg_attr(not(debug_assertions), inline(always))]
fn offset_fp(self: &Self, offset: StackOffset) -> StackAddress {
(self.fp as i64 + offset as i64) as StackAddress
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn offset_sp(self: &Self, offset: StackOffset) -> StackAddress {
(self.data.len() as i64 + offset as i64) as StackAddress
}
}
impl<T> StackRelativeOp<T> for Stack where Stack: StackOp<T> + StackOffsetOp<T> { }
#[allow(unused_macros)]
macro_rules! impl_stack {
(@push single, $type:tt, $stack: ident, $var:tt) => {
let bytes = $var.to_ne_bytes();
$stack.data.push(bytes[0]);
};
(@push multi, $type:tt, $stack: ident, $var:tt) => {
$stack.data.extend_from_slice(&$var.to_ne_bytes());
};
(@pop single, $type:tt, $stack: ident) => { {
let bytes = [ $stack.data.pop().unwrap() ];
$type::from_ne_bytes(bytes)
} };
(@pop multi, $type:tt, $stack: ident) => { {
let stack_len = $stack.data.len();
let start_pos = stack_len - size_of::<$type>();
let bytes = &$stack.data[start_pos .. stack_len];
let result = $type::from_ne_bytes(bytes.try_into().unwrap());
$stack.data.truncate(start_pos);
result
} };
(@store single, $type:tt, $stack: ident, $pos:expr, $var:tt) => {
let bytes = $var.to_ne_bytes();
$stack.data[$pos] = bytes[0];
};
(@store multi, $type:tt, $stack: ident, $pos:expr, $var:tt) => {
let bytes = $var.to_ne_bytes();
$stack.data[$pos .. $pos + size_of::<$type>()].copy_from_slice(&bytes);
};
(@load single, $type:tt, $stack: ident, $pos:expr) => { {
$type::from_ne_bytes([ $stack.data[$pos] ])
} };
(@load multi, $type:tt, $stack: ident, $pos:expr) => { {
let bytes = &$stack.data[$pos .. $pos + size_of::<$type>()];
$type::from_ne_bytes(bytes.try_into().unwrap())
} };
($size:tt, $type:tt) => {
impl StackOp<$type> for Stack {
#[cfg_attr(not(debug_assertions), inline(always))]
fn push(self: &mut Self, value: $type) {
impl_stack!(@push $size, $type, self, value);
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn pop(self: &mut Self) -> $type {
impl_stack!(@pop $size, $type, self)
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn store(self: &mut Self, pos: StackAddress, value: $type) {
impl_stack!(@store $size, $type, self, pos as usize, value);
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn load(self: &Self, pos: StackAddress) -> $type {
impl_stack!(@load $size, $type, self, pos as usize)
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn top(self: &Self) -> $type {
self.load(self.sp() - size_of::<$type>() as StackAddress)
}
}
};
}
impl_stack!(single, u8);
impl_stack!(single, i8);
impl_stack!(multi, u16);
impl_stack!(multi, i16);
impl_stack!(multi, u32);
impl_stack!(multi, i32);
impl_stack!(multi, f32);
impl_stack!(multi, u64);
impl_stack!(multi, i64);
impl_stack!(multi, f64);
impl_stack!(multi, usize);
impl_stack!(multi, isize);
impl_stack!(multi, HeapRef);