use std::cell::RefCell;
use std::collections::HashMap;
use cljrs_gc::GcPtr;
use cljrs_gc::Trace as _;
use cljrs_value::{Value, Var};
pub type VarKey = usize;
pub fn var_key_of(var: &GcPtr<Var>) -> VarKey {
var.get() as *const Var as usize
}
thread_local! {
static BINDING_STACK: RefCell<Vec<HashMap<VarKey, Value>>> =
const { RefCell::new(Vec::new()) };
}
pub struct BindingGuard;
impl Drop for BindingGuard {
fn drop(&mut self) {
pop_frame();
}
}
pub fn push_frame(bindings: HashMap<VarKey, Value>) -> BindingGuard {
BINDING_STACK.with(|s| s.borrow_mut().push(bindings));
BindingGuard
}
fn pop_frame() {
BINDING_STACK.with(|s| {
s.borrow_mut().pop();
});
}
pub fn deref_var(var: &GcPtr<Var>) -> Option<Value> {
let key = var_key_of(var);
let tl = BINDING_STACK.with(|s| {
s.borrow()
.iter()
.rev()
.find_map(|frame| frame.get(&key).cloned())
});
tl.or_else(|| var.get().deref())
}
pub fn is_thread_bound(var: &GcPtr<Var>) -> bool {
let key = var_key_of(var);
BINDING_STACK.with(|s| s.borrow().iter().any(|frame| frame.contains_key(&key)))
}
pub fn set_thread_local(var: &GcPtr<Var>, val: Value) -> bool {
let key = var_key_of(var);
BINDING_STACK.with(|s| {
for frame in s.borrow_mut().iter_mut().rev() {
if let std::collections::hash_map::Entry::Occupied(mut e) = frame.entry(key) {
e.insert(val);
return true;
}
}
false
})
}
pub fn capture_current() -> Vec<HashMap<VarKey, Value>> {
BINDING_STACK.with(|s| s.borrow().clone())
}
pub fn install_frames(frames: Vec<HashMap<VarKey, Value>>) {
BINDING_STACK.with(|s| *s.borrow_mut() = frames);
}
pub fn trace_current(visitor: &mut cljrs_gc::MarkVisitor) {
BINDING_STACK.with(|s| {
for frame in s.borrow().iter() {
for val in frame.values() {
val.trace(visitor);
}
}
});
}