cljrs_runtime/env/
dynamics.rs1use std::cell::RefCell;
8use std::collections::HashMap;
9
10use cljrs_gc::GcPtr;
11use cljrs_gc::Trace as _;
12use cljrs_value::{Value, Var};
13
14pub type VarKey = usize;
17
18pub fn var_key_of(var: &GcPtr<Var>) -> VarKey {
19 var.get() as *const Var as usize
20}
21
22struct BindingFrame {
23 values: HashMap<VarKey, Value>,
24 #[cfg(feature = "no-gc")]
25 leases: HashMap<VarKey, cljrs_value::publish::ValueLease>,
26}
27impl BindingFrame {
28 fn new(values: HashMap<VarKey, Value>) -> Self {
29 #[cfg(feature = "no-gc")]
30 let (values, leases) = {
31 let mut output = HashMap::new();
32 let mut leases = HashMap::new();
33 for (key, value) in values {
34 let (value, lease) =
35 cljrs_value::publish::prepare_for_owner(value, &Default::default());
36 output.insert(key, value);
37 leases.insert(key, lease);
38 }
39 (output, leases)
40 };
41 Self {
42 values,
43 #[cfg(feature = "no-gc")]
44 leases,
45 }
46 }
47}
48
49thread_local! {
50 static BINDING_STACK: RefCell<Vec<BindingFrame>> =
51 const { RefCell::new(Vec::new()) };
52}
53cljrs_gc::mutator_slot!("cljrs-runtime/BINDING_STACK", refcell BINDING_STACK: Vec<BindingFrame>, trace trace_saved);
54
55pub struct BindingGuard;
59
60impl Drop for BindingGuard {
61 fn drop(&mut self) {
62 pop_frame();
63 }
64}
65
66pub fn push_frame(bindings: HashMap<VarKey, Value>) -> BindingGuard {
70 let frame = BindingFrame::new(bindings);
71 BINDING_STACK.with(|s| s.borrow_mut().push(frame));
72 BindingGuard
73}
74
75pub fn take_frame(guard: BindingGuard) -> HashMap<VarKey, Value> {
80 std::mem::forget(guard);
81 BINDING_STACK.with(|s| {
82 s.borrow_mut()
83 .pop()
84 .map(|frame| {
85 #[cfg(feature = "no-gc")]
86 for lease in frame.leases.values() {
87 lease.protect_read();
88 }
89 frame.values
90 })
91 .unwrap_or_default()
92 })
93}
94
95fn pop_frame() {
96 BINDING_STACK.with(|s| {
97 s.borrow_mut().pop();
98 });
99}
100
101pub fn deref_var(var: &GcPtr<Var>) -> Option<Value> {
106 let key = var_key_of(var);
107 let tl = BINDING_STACK.with(|s| {
108 s.borrow().iter().rev().find_map(|frame| {
109 let value = frame.values.get(&key)?.clone();
110 #[cfg(feature = "no-gc")]
111 if let Some(lease) = frame.leases.get(&key) {
112 lease.protect_read();
113 }
114 Some(value)
115 })
116 });
117 tl.or_else(|| var.get().deref())
118}
119
120pub fn is_thread_bound(var: &GcPtr<Var>) -> bool {
122 let key = var_key_of(var);
123 BINDING_STACK.with(|s| {
124 s.borrow()
125 .iter()
126 .any(|frame| frame.values.contains_key(&key))
127 })
128}
129
130pub fn set_thread_local(var: &GcPtr<Var>, val: Value) -> bool {
134 let key = var_key_of(var);
135 #[cfg(feature = "no-gc")]
136 {
137 let target = BINDING_STACK.with(|stack| {
138 stack
139 .borrow()
140 .iter()
141 .rposition(|frame| frame.values.contains_key(&key))
142 });
143 let Some(index) = target else {
144 return false;
145 };
146 let (value, lease) = cljrs_value::publish::prepare_for_owner(val, &Default::default());
147 let old = BINDING_STACK.with(|stack| {
148 let mut stack = stack.borrow_mut();
149 stack[index].values.insert(key, value);
150 stack[index].leases.insert(key, lease)
151 });
152 drop(old);
153 true
154 }
155 #[cfg(not(feature = "no-gc"))]
156 BINDING_STACK.with(|s| {
157 for frame in s.borrow_mut().iter_mut().rev() {
158 if let std::collections::hash_map::Entry::Occupied(mut e) = frame.values.entry(key) {
159 e.insert(val);
160 return true;
161 }
162 }
163 false
164 })
165}
166
167pub fn capture_current() -> Vec<HashMap<VarKey, Value>> {
172 BINDING_STACK.with(|s| {
173 s.borrow()
174 .iter()
175 .map(|frame| {
176 #[cfg(feature = "no-gc")]
177 for lease in frame.leases.values() {
178 lease.protect_read();
179 }
180 frame.values.clone()
181 })
182 .collect()
183 })
184}
185
186pub fn install_frames(frames: Vec<HashMap<VarKey, Value>>) {
188 let frames = frames.into_iter().map(BindingFrame::new).collect();
189 BINDING_STACK.with(|s| *s.borrow_mut() = frames);
190}
191
192pub fn trace_current(visitor: &mut cljrs_gc::MarkVisitor) {
197 BINDING_STACK.with(|s| {
198 for frame in s.borrow().iter() {
199 for val in frame.values.values() {
200 val.trace(visitor);
201 }
202 }
203 });
204}
205
206fn trace_saved(frames: &Vec<BindingFrame>, visitor: &mut cljrs_gc::MarkVisitor) {
207 for frame in frames {
208 for value in frame.values.values() {
209 value.trace(visitor);
210 }
211 }
212}