cljrs_runtime/env/
callback.rs1use std::cell::RefCell;
9use std::sync::Arc;
10
11use cljrs_value::{Value, ValueError, ValueResult};
12
13use crate::env::env::{Env, GlobalEnv};
14
15struct EvalContext {
18 globals: Arc<GlobalEnv>,
19 current_ns: Arc<str>,
20 is_async: bool,
23}
24
25thread_local! {
26 static EVAL_CONTEXT: RefCell<Vec<EvalContext>> = const { RefCell::new(Vec::new()) };
27}
28cljrs_gc::mutator_slot!("cljrs-runtime/EVAL_CONTEXT", refcell EVAL_CONTEXT: Vec<EvalContext>, trace trace_saved);
29
30pub fn push_eval_context(env: &Env) {
32 EVAL_CONTEXT.with(|stack| {
33 stack.borrow_mut().push(EvalContext {
34 globals: env.globals.clone(),
35 current_ns: env.current_ns.clone(),
36 is_async: env.is_async,
37 });
38 });
39}
40
41pub(crate) fn scoped_eval_context(env: &Env) -> EvalContextGuard {
42 push_eval_context(env);
43 EvalContextGuard { _priv: () }
44}
45
46pub fn current_is_async() -> bool {
49 EVAL_CONTEXT.with(|stack| stack.borrow().last().is_some_and(|ec| ec.is_async))
50}
51
52pub fn pop_eval_context() {
54 EVAL_CONTEXT.with(|stack| {
55 stack.borrow_mut().pop();
56 });
57}
58
59pub fn capture_eval_context() -> Option<(Arc<GlobalEnv>, Arc<str>)> {
63 EVAL_CONTEXT.with(|stack| {
64 let s = stack.borrow();
65 let ec = s.last()?;
66 Some((ec.globals.clone(), ec.current_ns.clone()))
67 })
68}
69
70pub fn install_eval_context(globals: Arc<GlobalEnv>, ns: Arc<str>) {
74 EVAL_CONTEXT.with(|stack| {
75 stack.borrow_mut().push(EvalContext {
76 globals,
77 current_ns: ns,
78 is_async: false,
81 });
82 });
83}
84
85pub struct EvalContextGuard {
90 _priv: (),
91}
92
93impl Drop for EvalContextGuard {
94 fn drop(&mut self) {
95 pop_eval_context();
96 }
97}
98
99pub fn install_eval_context_guard(globals: Arc<GlobalEnv>, ns: Arc<str>) -> EvalContextGuard {
106 install_eval_context(globals, ns);
107 EvalContextGuard { _priv: () }
108}
109
110pub fn with_eval_context<F, R>(f: F) -> Result<R, crate::env::error::EvalError>
132where
133 F: FnOnce(&mut Env) -> Result<R, crate::env::error::EvalError>,
134{
135 let (globals, ns) = EVAL_CONTEXT.with(|stack| {
136 let s = stack.borrow();
137 let ec = s.last().ok_or_else(|| {
138 crate::env::error::EvalError::Runtime(
139 "with_eval_context called outside eval context".to_string(),
140 )
141 })?;
142 Ok::<_, crate::env::error::EvalError>((ec.globals.clone(), ec.current_ns.clone()))
143 })?;
144 let mut env = Env::new(globals, &ns);
145 f(&mut env)
146}
147
148pub fn invoke(f: &Value, args: Vec<Value>) -> ValueResult<Value> {
149 let (globals, ns) = EVAL_CONTEXT.with(|stack| {
150 let s = stack.borrow();
151 let ec = s
152 .last()
153 .ok_or_else(|| ValueError::Other("invoke called outside eval context".into()))?;
154 Ok((ec.globals.clone(), ec.current_ns.clone()))
155 })?;
156 let mut env = Env::new(globals, &ns);
157 let f = f.unwrap_meta();
161 let result = crate::interp::eval::with_read_scope(|| {
162 if let Value::Fn(cljx_fn) = f {
163 if let Some(fut) = crate::env::apply::dispatch_if_async(f, &args, &env) {
167 Ok(fut)
168 } else {
169 env.call_cljrs_fn(cljx_fn.get(), &args)
170 }
171 } else {
172 crate::env::apply::apply_value(f, args, &mut env)
173 }
174 });
175 result.map_err(eval_error_to_value_error)
176}
177
178pub fn eval_error_to_value_error(e: crate::env::error::EvalError) -> ValueError {
180 match e {
181 crate::env::error::EvalError::Thrown(v) => ValueError::Thrown(v),
182 crate::env::error::EvalError::GasExhausted => ValueError::GasExhausted,
183 crate::env::error::EvalError::Cancelled => ValueError::Cancelled,
184 crate::env::error::EvalError::MemoryLimitExceeded(fault) => {
185 cljrs_gc::governor::confirm_runtime_fault(fault);
189 ValueError::MemoryLimitExceeded(fault)
190 }
191 other => ValueError::Other(format!("{other}")),
192 }
193}
194
195fn trace_saved(contexts: &Vec<EvalContext>, visitor: &mut cljrs_gc::MarkVisitor) {
196 for context in contexts {
197 crate::env::gc_roots::trace_globals(&context.globals, visitor);
198 }
199}