1use crate::env::env::{Env, GlobalEnv};
2use crate::env::error::{EvalError, EvalResult};
3use cljrs_value::{Arity, MapValue, Value};
4use std::collections::{HashMap, HashSet};
5use std::sync::Arc;
6
7fn check_arity(arity: &Arity, argc: usize, name: &str) -> EvalResult<()> {
8 match arity {
9 Arity::Fixed(n) if argc != *n => Err(EvalError::Arity {
10 name: name.to_string(),
11 expected: n.to_string(),
12 got: argc,
13 }),
14 Arity::Variadic { min } if argc < *min => Err(EvalError::Arity {
15 name: name.to_string(),
16 expected: format!("{}+", min),
17 got: argc,
18 }),
19 _ => Ok(()),
20 }
21}
22
23#[derive(Default)]
24pub(crate) struct HierarchySnapshot {
25 pub(crate) generation: u64,
26 parents: Option<MapValue>,
27 ancestors: Option<MapValue>,
28}
29
30pub(crate) fn global_hierarchy_snapshot(globals: &GlobalEnv) -> HierarchySnapshot {
34 let Some(var) = globals.lookup_var("clojure.core", "global-hierarchy") else {
35 return HierarchySnapshot::default();
36 };
37 let generation = var.get().binding_generation();
38 let Some(Value::Map(hierarchy)) = var.get().deref() else {
39 return HierarchySnapshot {
40 generation,
41 ..HierarchySnapshot::default()
42 };
43 };
44 let relation = |name| {
45 hierarchy
46 .get(&Value::keyword(cljrs_value::Keyword::simple(name)))
47 .and_then(|value| match value {
48 Value::Map(map) => Some(map),
49 _ => None,
50 })
51 };
52 HierarchySnapshot {
53 generation,
54 parents: relation("parents"),
55 ancestors: relation("ancestors"),
56 }
57}
58
59fn isa_with_ancestors(child: &Value, parent: &Value, ancestors: Option<&MapValue>) -> bool {
60 if child == parent {
61 return true;
62 }
63 if let Some(ancestors) = ancestors
64 && let Some(Value::Set(of_child)) = ancestors.get(child)
65 && of_child.contains(parent)
66 {
67 return true;
68 }
69 if let (Value::Vector(c), Value::Vector(p)) = (child, parent) {
70 let (c, p) = (c.get(), p.get());
71 return c.count() == p.count()
72 && (0..c.count()).all(|i| match (c.nth(i), p.nth(i)) {
73 (Some(cv), Some(pv)) => isa_with_ancestors(cv, pv, ancestors),
74 _ => false,
75 });
76 }
77 false
78}
79
80fn prefers_with_table(
81 prefers: &HashMap<String, Vec<String>>,
82 parents: Option<&MapValue>,
83 x: &Value,
84 y: &Value,
85) -> bool {
86 fn recur(
87 prefers: &HashMap<String, Vec<String>>,
88 parents: Option<&MapValue>,
89 x: &Value,
90 y: &Value,
91 seen: &mut HashSet<(String, String)>,
92 ) -> bool {
93 let x_key = format!("{x}");
94 let y_key = format!("{y}");
95 if !seen.insert((x_key.clone(), y_key.clone())) {
96 return false;
97 }
98 if prefers
99 .get(&x_key)
100 .is_some_and(|over| over.contains(&y_key))
101 {
102 return true;
103 }
104 let Some(parents) = parents else {
105 return false;
106 };
107 if let Some(Value::Set(y_parents)) = parents.get(y)
108 && y_parents
109 .iter()
110 .any(|parent| recur(prefers, Some(parents), x, parent, seen))
111 {
112 return true;
113 }
114 if let Some(Value::Set(x_parents)) = parents.get(x)
115 && x_parents
116 .iter()
117 .any(|parent| recur(prefers, Some(parents), parent, y, seen))
118 {
119 return true;
120 }
121 false
122 }
123
124 recur(prefers, parents, x, y, &mut HashSet::new())
125}
126
127pub(crate) fn prefers_in_hierarchy(
130 mf: &cljrs_value::MultiFn,
131 x: &Value,
132 y: &Value,
133 hierarchy: &HierarchySnapshot,
134) -> bool {
135 let prefers = mf.prefers.lock().unwrap();
136 prefers_with_table(&prefers, hierarchy.parents.as_ref(), x, y)
137}
138
139fn join_conflicts(matches: &[&(String, Value)]) -> String {
140 match matches {
141 [] => String::new(),
142 [only] => only.0.clone(),
143 [first, second] => format!("{} and {}", first.0, second.0),
144 many => {
145 let (last, initial) = many.split_last().unwrap();
146 format!(
147 "{}, and {}",
148 initial
149 .iter()
150 .map(|entry| entry.0.as_str())
151 .collect::<Vec<_>>()
152 .join(", "),
153 last.0
154 )
155 }
156 }
157}
158
159fn hierarchy_method_key(
165 mf: &cljrs_value::MultiFn,
166 dispatch_val: &Value,
167 hierarchy: &HierarchySnapshot,
168) -> EvalResult<Option<String>> {
169 let dispatch_vals = mf.dispatch_vals.lock().unwrap();
170 let mut matches: Vec<(String, Value)> = dispatch_vals
171 .iter()
172 .filter(|(k, v)| {
173 k.as_str() != mf.default_dispatch
174 && isa_with_ancestors(dispatch_val, v, hierarchy.ancestors.as_ref())
175 })
176 .map(|(k, v)| (k.clone(), v.clone()))
177 .collect();
178 drop(dispatch_vals);
179 matches.sort_by(|a, b| a.0.cmp(&b.0));
180 if matches.len() <= 1 {
181 return Ok(matches.into_iter().next().map(|(k, _)| k));
182 }
183
184 let prefers = mf.prefers.lock().unwrap();
185 let dominates = |a: &(String, Value), b: &(String, Value)| {
186 prefers_with_table(&prefers, hierarchy.parents.as_ref(), &a.1, &b.1)
187 || isa_with_ancestors(&a.1, &b.1, hierarchy.ancestors.as_ref())
188 };
189 let best: Vec<&(String, Value)> = matches
190 .iter()
191 .filter(|a| {
192 !matches
193 .iter()
194 .any(|b| a.0 != b.0 && dominates(b, a) && !dominates(a, b))
195 })
196 .collect();
197 match best.as_slice() {
198 [only] => Ok(Some(only.0.clone())),
199 _ => Err(EvalError::Runtime(format!(
200 "Multiple methods in multimethod '{}' match dispatch value {}: {}, and {} is preferred",
201 mf.name,
202 dispatch_val,
203 join_conflicts(&best),
204 if best.len() == 2 { "neither" } else { "none" }
205 ))),
206 }
207}
208
209pub fn type_tag_of(val: &Value) -> Arc<str> {
211 match val.unwrap_meta() {
212 Value::Nil => Arc::from("nil"),
213 Value::Bool(_) => Arc::from("Boolean"),
214 Value::Long(_) => Arc::from("Long"),
215 Value::Double(_) => Arc::from("Double"),
216 Value::BigInt(_) => Arc::from("BigInt"),
217 Value::BigDecimal(_) => Arc::from("BigDecimal"),
218 Value::Ratio(_) => Arc::from("Ratio"),
219 Value::Char(_) => Arc::from("Character"),
220 Value::Str(_) => Arc::from("String"),
221 Value::Keyword(_) => Arc::from("Keyword"),
222 Value::Symbol(_) => Arc::from("Symbol"),
223 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => Arc::from("List"),
224 Value::Vector(_) => Arc::from("Vector"),
225 Value::Map(_) => Arc::from("Map"),
226 Value::Set(_) => Arc::from("Set"),
227 Value::Fn(_) | Value::NativeFunction(_) | Value::ProtocolFn(_) | Value::MultiFn(_) => {
228 Arc::from("Fn")
229 }
230 Value::Atom(_) => Arc::from("Atom"),
231 Value::Var(_) => Arc::from("Var"),
232 Value::Protocol(_) => Arc::from("Protocol"),
233 Value::Volatile(_) => Arc::from("Volatile"),
234 Value::Delay(_) => Arc::from("Delay"),
235 Value::Promise(_) => Arc::from("Promise"),
236 Value::Future(_) => Arc::from("Future"),
237 Value::Agent(_) => Arc::from("Agent"),
238 Value::TypeInstance(ti) => ti.get().type_tag.clone(),
239 Value::NativeObject(obj) => Arc::from(obj.get().type_tag()),
240 Value::Resource(_) => Arc::from("Resource"),
241 _ => Arc::from("Object"),
242 }
243}
244
245pub fn type_tag_matches(val: &Value, tag: &str) -> bool {
251 let val = val.unwrap_meta();
255 match val {
256 Value::TypeInstance(ti) => &*ti.get().type_tag == tag,
257 Value::NativeObject(obj) => obj.get().type_tag() == tag,
258 _ => {
259 match val {
262 Value::Nil => "nil",
263 Value::Bool(_) => "Boolean",
264 Value::Long(_) => "Long",
265 Value::Double(_) => "Double",
266 Value::BigInt(_) => "BigInt",
267 Value::BigDecimal(_) => "BigDecimal",
268 Value::Ratio(_) => "Ratio",
269 Value::Char(_) => "Character",
270 Value::Str(_) => "String",
271 Value::Keyword(_) => "Keyword",
272 Value::Symbol(_) => "Symbol",
273 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => "List",
274 Value::Vector(_) => "Vector",
275 Value::Map(_) => "Map",
276 Value::Set(_) => "Set",
277 Value::Fn(_)
278 | Value::NativeFunction(_)
279 | Value::ProtocolFn(_)
280 | Value::MultiFn(_) => "Fn",
281 Value::Atom(_) => "Atom",
282 Value::Var(_) => "Var",
283 Value::Protocol(_) => "Protocol",
284 Value::Volatile(_) => "Volatile",
285 Value::Delay(_) => "Delay",
286 Value::Promise(_) => "Promise",
287 Value::Future(_) => "Future",
288 Value::Agent(_) => "Agent",
289 Value::Resource(_) => "Resource",
290 _ => "Object",
291 }
292 }
293 .eq(tag),
294 }
295}
296
297pub fn dispatch_if_async(callee: &Value, args: &[Value], env: &Env) -> Option<Value> {
305 let Value::Fn(f) = callee else { return None };
306 if !f.get().is_async {
307 return None;
308 }
309 let rt = env.globals.async_runtime()?;
310 let call_env = Env::new(env.globals.clone(), &env.current_ns);
311 Some(rt.spawn_async_call(callee.clone(), args.to_vec(), call_env))
312}
313
314pub fn apply_value(callee: &Value, args: Vec<Value>, env: &mut Env) -> EvalResult {
316 let _callee_root = crate::env::gc_roots::root_value(callee);
319 let _args_root = crate::env::gc_roots::root_values(&args);
320
321 crate::env::gc_roots::gc_safepoint(env);
324
325 match callee {
326 Value::NativeFunction(nf) => {
327 crate::env::policy::check_native(&nf.get().name)?;
328 check_arity(&nf.get().arity, args.len(), &nf.get().name)?;
329 let _caller_root = crate::env::gc_roots::push_env_root(env);
333 crate::env::callback::push_eval_context(env);
334 let result =
335 (nf.get().func)(&args).map_err(crate::env::error::value_error_to_eval_error);
336 crate::env::callback::pop_eval_context();
337 result
338 }
339 Value::Fn(f) => {
340 if let Some(fut) = dispatch_if_async(callee, &args, env) {
341 return Ok(fut);
342 }
343 env.call_cljrs_fn(f.get(), &args)
344 }
345 Value::BoundFn(bf) => {
346 let bf_ref = bf.get();
347 let _guard = crate::env::dynamics::push_frame(bf_ref.captured_bindings.clone());
351 apply_value(&bf_ref.wrapped, args, env)
352 }
353 Value::ProtocolFn(pf) => {
354 let pf_ref = pf.get();
355 let dispatch_val = args.first().ok_or_else(|| {
356 EvalError::Runtime(format!(
357 "{}: requires at least 1 argument",
358 pf_ref.method_name
359 ))
360 })?;
361
362 if pf_ref.protocol.get().extend_via_metadata
373 && let Some(Value::Map(m)) = dispatch_val.get_meta()
374 {
375 let proto = pf_ref.protocol.get();
376 let method_sym = Value::Symbol(cljrs_gc::GcPtr::new(
377 cljrs_value::Symbol::qualified(proto.ns.clone(), pf_ref.method_name.clone()),
378 ));
379 if let Some(impl_fn) = m.get(&method_sym) {
380 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
381 return apply_value(&impl_fn, args, env);
382 }
383 }
384
385 let tag = type_tag_of(dispatch_val);
386 let impls = pf_ref.protocol.get().impls.lock().unwrap();
387 let impl_fn = impls
388 .get(tag.as_ref())
389 .and_then(|m| m.get(pf_ref.method_name.as_ref()))
390 .cloned()
391 .ok_or_else(|| {
392 EvalError::Runtime(format!(
393 "No implementation of protocol {} for type {}",
394 pf_ref.protocol.get().name,
395 tag
396 ))
397 })?;
398 drop(impls);
399 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
400 apply_value(&impl_fn, args, env)
401 }
402 Value::MultiFn(mf) => {
403 let mf_ref = mf.get();
404 let dispatch_val = apply_value(&mf_ref.dispatch_fn, args.clone(), env)?;
405 let _dispatch_root = crate::env::gc_roots::root_value(&dispatch_val);
406 cljrs_gc::safepoint();
407 let key = format!("{}", dispatch_val);
408 let exact = mf_ref.methods.lock().unwrap().get(&key).cloned();
409 let impl_fn = match exact {
410 Some(f) => f,
411 None => {
412 let hierarchy = global_hierarchy_snapshot(&env.globals);
413 let method_generation = mf_ref.method_generation();
414 let cached =
415 mf_ref.cached_method(&key, hierarchy.generation, method_generation);
416 let method_key = match cached {
417 Some(cached) => cached,
418 None => {
419 let inherited =
420 hierarchy_method_key(mf_ref, &dispatch_val, &hierarchy)?;
421 let resolved = match inherited {
422 Some(k) => k,
423 None if mf_ref
424 .methods
425 .lock()
426 .unwrap()
427 .contains_key(&mf_ref.default_dispatch) =>
428 {
429 mf_ref.default_dispatch.clone()
430 }
431 None => {
432 return Err(EvalError::Runtime(format!(
433 "No method in multimethod '{}' for dispatch value {}",
434 mf_ref.name, key
435 )));
436 }
437 };
438 mf_ref.cache_method(
439 key.clone(),
440 resolved.clone(),
441 hierarchy.generation,
442 method_generation,
443 );
444 resolved
445 }
446 };
447 mf_ref
448 .methods
449 .lock()
450 .unwrap()
451 .get(&method_key)
452 .cloned()
453 .ok_or_else(|| {
454 EvalError::Runtime(format!(
455 "No method in multimethod '{}' for dispatch value {}",
456 mf_ref.name, key
457 ))
458 })?
459 }
460 };
461 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
462 apply_value(&impl_fn, args, env)
463 }
464 Value::Keyword(_kw) => {
465 let default = || args.get(1).cloned().unwrap_or(Value::Nil);
467 let target = args.first().map(|a| a.unwrap_meta());
468 match target {
469 Some(Value::Map(m)) => Ok(m.get(callee).unwrap_or_else(default)),
470 Some(Value::TypeInstance(ti)) => {
471 Ok(ti.get().fields.get(callee).unwrap_or_else(default))
472 }
473 Some(Value::Nil) => Ok(default()),
474 _ => Ok(Value::Nil),
475 }
476 }
477 Value::Map(m) => {
478 match args.first() {
480 Some(k) => Ok(m
481 .get(k)
482 .unwrap_or(args.get(1).cloned().unwrap_or(Value::Nil))),
483 None => Ok(Value::Nil),
484 }
485 }
486 Value::Set(s) => match args.first() {
487 Some(k) => {
488 if s.contains(k) {
489 Ok(k.clone())
490 } else {
491 Ok(Value::Nil)
492 }
493 }
494 None => Ok(Value::Nil),
495 },
496 Value::WithMeta(inner, _) => apply_value(inner, args, env),
497 Value::Var(v) => {
498 let inner = crate::env::dynamics::deref_var(v).ok_or_else(|| {
503 EvalError::Runtime(format!(
504 "unbound var {}/{} used as function",
505 v.get().namespace,
506 v.get().name,
507 ))
508 })?;
509 apply_value(&inner, args, env)
510 }
511 other => Err(EvalError::NotCallable(format!(
512 "<{}> is not callable",
513 other.type_name()
514 ))),
515 }
516}