use std::mem::{MaybeUninit, replace, transmute}; use std::ops::Deref; use crate::error::*; macro_rules! into { ($e:expr) => { // SAFETY: This macro is used to transmute `MaybeUninit>` to `Value<'vm>` // or `&MaybeUninit>` to `&Value<'vm>`. // This is safe because the `Stack` ensures that only initialized values are accessed // within the `0..top` range. unsafe { transmute($e) } }; } pub struct Stack { items: Box<[MaybeUninit; CAP]>, top: usize, } impl Default for Stack { fn default() -> Self { Self::new() } } impl Stack { pub fn new() -> Self { Stack { items: unsafe { std::mem::transmute::>, Box<[MaybeUninit; CAP]>>( Box::new_uninit(), ) }, top: 0, } } pub fn push(&mut self, item: T) -> Result<()> { self.items .get_mut(self.top) .map_or_else(|| Err(Error::EvalError("stack overflow".to_string())), Ok)? .write(item); self.top += 1; Ok(()) } pub fn pop(&mut self) -> T { let item = self.items.get_mut(self.top - 1).unwrap(); self.top -= 1; // SAFETY: `item` at `self.top` was previously written and is initialized. // We replace it with `MaybeUninit::uninit()` and then `assume_init` // on the original value, which is safe as it was initialized. unsafe { replace(item, MaybeUninit::uninit()).assume_init() } } pub fn tos(&self) -> &T { into!(&self.items[self.top - 1]) } pub fn tos_mut(&mut self) -> &mut T { into!(&mut self.items[self.top - 1]) } } impl Deref for Stack { type Target = [T]; fn deref(&self) -> &Self::Target { into!(&self.items[0..self.top]) } } impl Drop for Stack { fn drop(&mut self) { self.items.as_mut_slice()[0..self.top] .iter_mut() // SAFETY: Items in the range `0..self.top` are guaranteed to be initialized. // `assume_init_drop` is called to correctly drop these initialized `Value`s. .map(|item| unsafe { item.assume_init_drop() }) .for_each(drop) } }