///|
/// @types.Value stack for operands
struct Stack {
values : Array[@types.Value]
limit : Int32
mut reserved : Int32
mut sp : Int32 // stack pointer
} derive(Debug)
///|
pub fn Stack::Stack(max_values~ : Int32 = 10000) -> Stack {
{ values: [], sp: 0, limit: max_values, reserved: 0, }
}
///|
pub fn Stack::push(
self : Stack,
value : @types.Value,
) -> Unit raise RuntimeError {
if self.sp >= self.limit - self.reserved {
raise StackOverflow
}
if self.sp >= self.values.length() {
self.values.push(value)
} else {
self.values[self.sp] = value
}
self.sp = self.sp + 1
}
///|
pub fn Stack::pop(self : Stack) -> @types.Value raise RuntimeError {
if self.sp <= 0 {
raise StackUnderflow
}
self.sp = self.sp - 1
let value = self.values[self.sp]
self.values[self.sp] = I32(0)
value
}
///|
pub fn Stack::peek(self : Stack) -> @types.Value raise RuntimeError {
if self.sp <= 0 {
raise StackUnderflow
}
self.values[self.sp - 1]
}
///|
pub fn Stack::pop_i32(self : Stack) -> Int32 raise RuntimeError {
match self.pop() {
I32(n) => n
v => {
@logger.debug("type mismatch: expected i32, got \{v}")
raise TypeMismatch
}
}
}
///|
pub fn Stack::pop_i64(self : Stack) -> Int64 raise RuntimeError {
match self.pop() {
I64(n) => n
v => {
@logger.debug("type mismatch: expected i64, got \{v}")
raise TypeMismatch
}
}
}
///|
pub fn Stack::pop_f32(self : Stack) -> Float raise RuntimeError {
match self.pop() {
F32(f) => f
v => {
@logger.debug("type mismatch: expected f32, got \{v}")
raise TypeMismatch
}
}
}
///|
pub fn Stack::pop_f64(self : Stack) -> Double raise RuntimeError {
match self.pop() {
F64(f) => f
v => {
@logger.debug("type mismatch: expected f64, got \{v}")
raise TypeMismatch
}
}
}
///|
pub fn Stack::push_v128(self : Stack, v : Bytes) -> Unit raise RuntimeError {
self.push(V128(v))
}
///|
pub fn Stack::pop_v128(self : Stack) -> Bytes raise RuntimeError {
match self.pop() {
V128(v) => v
v => {
@logger.debug("type mismatch: expected v128, got \{v}")
raise TypeMismatch
}
}
}
///|
pub fn Stack::size(self : Stack) -> Int32 {
self.sp
}
///|
/// Snapshot currently live stack values [0, sp)
pub fn Stack::active_values(self : Stack) -> Array[@types.Value] {
let active : Array[@types.Value] = []
for i in 0.. Unit {
self.sp = 0
self.reserved = 0
self.values.clear()
}
///|
/// Pop n values from the stack, returning them in order (first popped is last in array)
pub fn Stack::pop_n(
self : Stack,
n : Int32,
) -> Array[@types.Value] raise RuntimeError {
if n <= 0 {
return []
}
if self.sp < n {
raise StackUnderflow
}
let values : Array[@types.Value] = Array::make(n, I32(0))
// Pop in reverse order so the array maintains stack order
for i = n - 1; i >= 0; i = i - 1 {
self.sp = self.sp - 1
values[i] = self.values[self.sp]
self.values[self.sp] = I32(0)
}
values
}
///|
/// Count frame locals against the same budget as live operand slots.
pub fn Stack::reserve(self : Stack, count : Int32) -> Unit raise RuntimeError {
if count < 0 || count > self.limit - self.reserved - self.sp {
raise StackOverflow
}
self.reserved += count
}
///|
pub fn Stack::release(self : Stack, count : Int32) -> Unit {
self.reserved -= count
}