use runmat_builtins::Value;
use runmat_gc::{
gc_register_root, gc_unregister_root, GlobalRoot, RootId, StackRoot, VariableArrayRoot,
};
use std::marker::PhantomData;
use std::ptr::NonNull;
type RootLifetime<'roots> = fn(&'roots Vec<Value>, &'roots Vec<Value>);
pub struct InterpretContext<'roots> {
pub(crate) stack_root_id: Option<RootId>,
pub(crate) vars_root_id: Option<RootId>,
pub(crate) extra_root_ids: Vec<RootId>,
_roots: PhantomData<RootLifetime<'roots>>,
}
impl<'roots> InterpretContext<'roots> {
pub fn new(stack: &'roots Vec<Value>, vars: &'roots Vec<Value>) -> Result<Self, String> {
let stack_root = Box::new(unsafe {
StackRoot::new(NonNull::from(stack), "interpreter_stack".to_string())
});
let vars_root = Box::new(unsafe {
VariableArrayRoot::new(NonNull::from(vars), "interpreter_vars".to_string())
});
let stack_root_id = gc_register_root(stack_root)
.map_err(|e| format!("Failed to register stack root: {e:?}"))?;
let vars_root_id = gc_register_root(vars_root)
.map_err(|e| format!("Failed to register vars root: {e:?}"))?;
Ok(InterpretContext {
stack_root_id: Some(stack_root_id),
vars_root_id: Some(vars_root_id),
extra_root_ids: Vec::new(),
_roots: PhantomData,
})
}
pub fn register_global_values(
&mut self,
values: Vec<Value>,
description: &str,
) -> Result<(), String> {
if values.is_empty() {
return Ok(());
}
let root = Box::new(GlobalRoot::new(values, description.to_string()));
let id =
gc_register_root(root).map_err(|e| format!("Failed to register global root: {e:?}"))?;
self.extra_root_ids.push(id);
Ok(())
}
}
impl Drop for InterpretContext<'_> {
fn drop(&mut self) {
if let Some(id) = self.stack_root_id.take() {
let _ = gc_unregister_root(id);
}
if let Some(id) = self.vars_root_id.take() {
let _ = gc_unregister_root(id);
}
while let Some(id) = self.extra_root_ids.pop() {
let _ = gc_unregister_root(id);
}
}
}