1use crate::RuntimeError;
2use runmat_thread_local::runmat_thread_local;
3use runmat_types::{CallableFallbackPolicy, CallableIdentity, SourceId};
4use runmat_value::Value;
5use std::cell::RefCell;
6use std::future::Future;
7use std::pin::Pin;
8use std::rc::Rc;
9use std::sync::Arc;
10
11pub type UserFunctionFuture = Pin<Box<dyn Future<Output = Result<Value, RuntimeError>>>>;
12pub type DynamicFunctionLoadFuture =
13 Pin<Box<dyn Future<Output = Option<Result<Value, RuntimeError>>>>>;
14pub type FunctionInvoker = dyn Fn(usize, &[Value], usize) -> UserFunctionFuture;
15#[derive(Debug, Clone)]
16pub struct ExternalFunctionCall {
17 pub function: usize,
18 pub display_name: String,
19 pub arguments: Vec<Value>,
20 pub requested_outputs: usize,
21}
22pub type ExternalFunctionInvoker = dyn Fn(ExternalFunctionCall) -> UserFunctionFuture;
23pub type LexicalFunctionFuture =
24 Pin<Box<dyn Future<Output = Result<crate::call::lexical::LexicalCallResult, RuntimeError>>>>;
25pub type LexicalFunctionInvoker =
26 dyn Fn(crate::call::lexical::LexicalCall) -> LexicalFunctionFuture;
27pub type FunctionResolver = dyn Fn(&str) -> Option<usize> + Send + Sync;
28pub type DynamicFunctionLoader =
29 dyn Fn(String, Vec<Value>, usize) -> DynamicFunctionLoadFuture + Send + Sync;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct SourceFunctionInfo {
33 pub source_id: SourceId,
34 pub name: String,
35 pub function: usize,
36}
37
38#[derive(Debug, Clone)]
39pub struct CallableRequest {
40 identity: CallableIdentity,
41 fallback_policy: CallableFallbackPolicy,
42 args: Vec<Value>,
43 requested_outputs: usize,
44}
45
46impl CallableRequest {
47 pub fn semantic(function: usize, args: Vec<Value>, requested_outputs: usize) -> Self {
48 Self {
49 identity: CallableIdentity::BoundFunction(runmat_types::FunctionId(function)),
50 fallback_policy: CallableFallbackPolicy::None,
51 args,
52 requested_outputs,
53 }
54 }
55
56 pub fn resolved(
57 identity: CallableIdentity,
58 fallback_policy: CallableFallbackPolicy,
59 args: Vec<Value>,
60 requested_outputs: usize,
61 ) -> Self {
62 Self {
63 identity,
64 fallback_policy,
65 args,
66 requested_outputs,
67 }
68 }
69}
70
71runmat_thread_local! {
72 static SEMANTIC_FUNCTION_INVOKER: RefCell<Option<Rc<FunctionInvoker>>> =
73 const { RefCell::new(None) };
74 static EXTERNAL_FUNCTION_INVOKER: RefCell<Option<Rc<ExternalFunctionInvoker>>> =
75 const { RefCell::new(None) };
76 static LEXICAL_FUNCTION_INVOKER: RefCell<Option<Rc<LexicalFunctionInvoker>>> =
77 const { RefCell::new(None) };
78 static SEMANTIC_FUNCTION_RESOLVER: RefCell<Option<Arc<FunctionResolver>>> =
79 const { RefCell::new(None) };
80 static SOURCE_FUNCTION_CATALOG: RefCell<Option<Arc<Vec<SourceFunctionInfo>>>> =
81 const { RefCell::new(None) };
82 static ACTIVE_SEMANTIC_FUNCTION_STACK: RefCell<Vec<usize>> =
83 const { RefCell::new(Vec::new()) };
84}
85
86pub struct FunctionInvokerGuard {
87 previous: Option<Rc<FunctionInvoker>>,
88 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
89}
90
91pub struct ExternalFunctionInvokerGuard {
92 previous: Option<Rc<ExternalFunctionInvoker>>,
93 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
94}
95
96pub struct LexicalFunctionInvokerGuard {
97 previous: Option<Rc<LexicalFunctionInvoker>>,
98 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
99}
100
101pub struct FunctionResolverGuard {
102 previous: Option<Arc<FunctionResolver>>,
103 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
104}
105
106pub struct SourceFunctionCatalogGuard {
107 previous: Option<Arc<Vec<SourceFunctionInfo>>>,
108 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
109}
110
111pub struct ActiveSemanticFunctionGuard {
112 state: Option<std::rc::Rc<crate::context::RuntimeContextState>>,
113}
114
115impl Drop for FunctionInvokerGuard {
116 fn drop(&mut self) {
117 let previous = self.previous.take();
118 if let Some(state) = &self.state {
119 state.call.borrow_mut().semantic_invoker = previous;
120 } else {
121 SEMANTIC_FUNCTION_INVOKER.with(|slot| {
122 *slot.borrow_mut() = previous;
123 });
124 }
125 }
126}
127
128impl Drop for ExternalFunctionInvokerGuard {
129 fn drop(&mut self) {
130 let previous = self.previous.take();
131 if let Some(state) = &self.state {
132 state.call.borrow_mut().external_invoker = previous;
133 } else {
134 EXTERNAL_FUNCTION_INVOKER.with(|slot| {
135 *slot.borrow_mut() = previous;
136 });
137 }
138 }
139}
140
141impl Drop for LexicalFunctionInvokerGuard {
142 fn drop(&mut self) {
143 let previous = self.previous.take();
144 if let Some(state) = &self.state {
145 state.call.borrow_mut().lexical_invoker = previous;
146 } else {
147 LEXICAL_FUNCTION_INVOKER.with(|slot| {
148 *slot.borrow_mut() = previous;
149 });
150 }
151 }
152}
153
154impl Drop for FunctionResolverGuard {
155 fn drop(&mut self) {
156 let previous = self.previous.take();
157 if let Some(state) = &self.state {
158 state.call.borrow_mut().semantic_resolver = previous;
159 } else {
160 SEMANTIC_FUNCTION_RESOLVER.with(|slot| {
161 *slot.borrow_mut() = previous;
162 });
163 }
164 }
165}
166
167impl Drop for SourceFunctionCatalogGuard {
168 fn drop(&mut self) {
169 let previous = self.previous.take();
170 if let Some(state) = &self.state {
171 state.call.borrow_mut().source_functions = previous;
172 } else {
173 SOURCE_FUNCTION_CATALOG.with(|slot| {
174 *slot.borrow_mut() = previous;
175 });
176 }
177 }
178}
179
180impl Drop for ActiveSemanticFunctionGuard {
181 fn drop(&mut self) {
182 if let Some(state) = &self.state {
183 state.call.borrow_mut().active_functions.pop();
184 } else {
185 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
186 slot.borrow_mut().pop();
187 });
188 }
189 }
190}
191
192pub fn install_semantic_function_invoker(
193 invoker: Option<Arc<FunctionInvoker>>,
194) -> FunctionInvokerGuard {
195 replace_semantic_function_invoker(invoker.map(|invoker| {
196 Rc::new(move |function, arguments: &[Value], requested_outputs| {
197 invoker(function, arguments, requested_outputs)
198 }) as Rc<FunctionInvoker>
199 }))
200}
201
202pub fn install_local_semantic_function_invoker(
203 invoker: Rc<FunctionInvoker>,
204) -> FunctionInvokerGuard {
205 replace_semantic_function_invoker(Some(invoker))
206}
207
208pub fn clear_semantic_function_invoker() -> FunctionInvokerGuard {
209 replace_semantic_function_invoker(None)
210}
211
212fn replace_semantic_function_invoker(invoker: Option<Rc<FunctionInvoker>>) -> FunctionInvokerGuard {
213 if let Some(state) = active_state() {
214 let previous = std::mem::replace(&mut state.call.borrow_mut().semantic_invoker, invoker);
215 return FunctionInvokerGuard {
216 previous,
217 state: Some(state),
218 };
219 }
220 let previous =
221 SEMANTIC_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
222 FunctionInvokerGuard {
223 previous,
224 state: None,
225 }
226}
227
228pub fn install_external_function_invoker(
229 invoker: Option<Arc<ExternalFunctionInvoker>>,
230) -> ExternalFunctionInvokerGuard {
231 replace_external_function_invoker(
232 invoker.map(|invoker| Rc::new(move |call| invoker(call)) as Rc<ExternalFunctionInvoker>),
233 )
234}
235
236pub fn install_local_external_function_invoker(
237 invoker: Rc<ExternalFunctionInvoker>,
238) -> ExternalFunctionInvokerGuard {
239 replace_external_function_invoker(Some(invoker))
240}
241
242fn replace_external_function_invoker(
243 invoker: Option<Rc<ExternalFunctionInvoker>>,
244) -> ExternalFunctionInvokerGuard {
245 if let Some(state) = active_state() {
246 let previous = std::mem::replace(&mut state.call.borrow_mut().external_invoker, invoker);
247 return ExternalFunctionInvokerGuard {
248 previous,
249 state: Some(state),
250 };
251 }
252 let previous =
253 EXTERNAL_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
254 ExternalFunctionInvokerGuard {
255 previous,
256 state: None,
257 }
258}
259
260pub fn install_lexical_function_invoker(
261 invoker: Option<Arc<LexicalFunctionInvoker>>,
262) -> LexicalFunctionInvokerGuard {
263 replace_lexical_function_invoker(
264 invoker.map(|invoker| Rc::new(move |call| invoker(call)) as Rc<LexicalFunctionInvoker>),
265 )
266}
267
268pub fn install_local_lexical_function_invoker(
269 invoker: Rc<LexicalFunctionInvoker>,
270) -> LexicalFunctionInvokerGuard {
271 replace_lexical_function_invoker(Some(invoker))
272}
273
274fn replace_lexical_function_invoker(
275 invoker: Option<Rc<LexicalFunctionInvoker>>,
276) -> LexicalFunctionInvokerGuard {
277 if let Some(state) = active_state() {
278 let previous = std::mem::replace(&mut state.call.borrow_mut().lexical_invoker, invoker);
279 return LexicalFunctionInvokerGuard {
280 previous,
281 state: Some(state),
282 };
283 }
284 let previous =
285 LEXICAL_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
286 LexicalFunctionInvokerGuard {
287 previous,
288 state: None,
289 }
290}
291
292pub fn install_semantic_function_resolver(
293 resolver: Option<Arc<FunctionResolver>>,
294) -> FunctionResolverGuard {
295 if let Some(state) = active_state() {
296 let previous = std::mem::replace(&mut state.call.borrow_mut().semantic_resolver, resolver);
297 return FunctionResolverGuard {
298 previous,
299 state: Some(state),
300 };
301 }
302 let previous = SEMANTIC_FUNCTION_RESOLVER
303 .with(|slot| std::mem::replace(&mut *slot.borrow_mut(), resolver));
304 FunctionResolverGuard {
305 previous,
306 state: None,
307 }
308}
309
310pub fn install_source_function_catalog(
311 catalog: Option<Arc<Vec<SourceFunctionInfo>>>,
312) -> SourceFunctionCatalogGuard {
313 if let Some(state) = active_state() {
314 let previous = std::mem::replace(&mut state.call.borrow_mut().source_functions, catalog);
315 return SourceFunctionCatalogGuard {
316 previous,
317 state: Some(state),
318 };
319 }
320 let previous =
321 SOURCE_FUNCTION_CATALOG.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), catalog));
322 SourceFunctionCatalogGuard {
323 previous,
324 state: None,
325 }
326}
327
328pub fn push_active_semantic_function(function: usize) -> ActiveSemanticFunctionGuard {
329 if let Some(state) = active_state() {
330 state.call.borrow_mut().active_functions.push(function);
331 return ActiveSemanticFunctionGuard { state: Some(state) };
332 }
333 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
334 slot.borrow_mut().push(function);
335 });
336 ActiveSemanticFunctionGuard { state: None }
337}
338
339pub fn current_semantic_function_invoker() -> Option<Rc<FunctionInvoker>> {
340 if let Some(state) = active_state() {
341 return state.call.borrow().semantic_invoker.clone();
342 }
343 SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
344}
345
346pub fn current_external_function_invoker() -> Option<Rc<ExternalFunctionInvoker>> {
347 if let Some(state) = active_state() {
348 return state.call.borrow().external_invoker.clone();
349 }
350 EXTERNAL_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
351}
352
353pub fn current_lexical_function_invoker() -> Option<Rc<LexicalFunctionInvoker>> {
354 if let Some(state) = active_state() {
355 return state.call.borrow().lexical_invoker.clone();
356 }
357 LEXICAL_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
358}
359
360pub fn current_semantic_function_resolver() -> Option<Arc<FunctionResolver>> {
361 if let Some(state) = active_state() {
362 return state.call.borrow().semantic_resolver.clone();
363 }
364 SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())
365}
366
367pub fn current_active_semantic_function() -> Option<usize> {
368 if let Some(state) = active_state() {
369 return state.call.borrow().active_functions.last().copied();
370 }
371 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().last().copied())
372}
373
374pub fn inherit_legacy_call_environment(context: &crate::context::RuntimeContext) {
381 let mut call = context.state().call.borrow_mut();
382 call.semantic_invoker = SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
383 call.external_invoker = EXTERNAL_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
384 call.lexical_invoker = LEXICAL_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
385 call.semantic_resolver = SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone());
386 call.source_functions = SOURCE_FUNCTION_CATALOG.with(|slot| slot.borrow().clone());
387 call.active_functions = ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().clone());
388}
389
390pub fn source_functions_for(source_id: SourceId) -> Vec<SourceFunctionInfo> {
391 if let Some(state) = active_state() {
392 return source_functions_in_catalog(
393 state.call.borrow().source_functions.as_deref(),
394 source_id,
395 );
396 }
397 SOURCE_FUNCTION_CATALOG
398 .with(|slot| source_functions_in_catalog(slot.borrow().as_deref(), source_id))
399}
400
401pub async fn try_call_semantic_function(
402 function: usize,
403 args: &[Value],
404 requested_outputs: usize,
405) -> Option<Result<Value, RuntimeError>> {
406 let invoker = current_semantic_function_invoker();
407 let invoker = invoker?;
408 Some(invoker(function, args, requested_outputs).await)
409}
410
411pub async fn try_call_external_function(
412 call: ExternalFunctionCall,
413) -> Option<Result<Value, RuntimeError>> {
414 let invoker = current_external_function_invoker()?;
415 Some(invoker(call).await)
416}
417
418pub async fn try_call_lexical_function(
419 call: crate::call::lexical::LexicalCall,
420) -> Option<Result<crate::call::lexical::LexicalCallResult, RuntimeError>> {
421 let invoker = current_lexical_function_invoker()?;
422 Some(invoker(call).await)
423}
424
425pub async fn try_call_semantic_function_by_name(
426 name: &str,
427 args: &[Value],
428 requested_outputs: usize,
429) -> Option<Result<Value, RuntimeError>> {
430 let function = resolve_semantic_function_by_name(name)?;
431 try_call_semantic_function(function, args, requested_outputs).await
432}
433
434pub fn resolve_semantic_function_by_name(name: &str) -> Option<usize> {
435 let resolver = current_semantic_function_resolver()?;
436 resolver(name)
437}
438
439pub async fn try_load_and_call_dynamic_function(
440 name: String,
441 args: Vec<Value>,
442 requested_outputs: usize,
443) -> Option<Result<Value, RuntimeError>> {
444 let loader = crate::context::legacy::active()?
445 .state()
446 .call
447 .borrow()
448 .dynamic_loader
449 .clone()?;
450 loader(name, args, requested_outputs).await
451}
452
453fn source_functions_in_catalog(
454 catalog: Option<&Vec<SourceFunctionInfo>>,
455 source_id: SourceId,
456) -> Vec<SourceFunctionInfo> {
457 catalog
458 .map(|catalog| {
459 catalog
460 .iter()
461 .filter(|info| info.source_id == source_id)
462 .cloned()
463 .collect()
464 })
465 .unwrap_or_default()
466}
467
468fn active_state() -> Option<std::rc::Rc<crate::context::RuntimeContextState>> {
469 crate::context::legacy::active().map(|context| std::rc::Rc::clone(context.state()))
470}
471
472pub async fn try_call_semantic_descriptor(
473 request: CallableRequest,
474) -> Option<Result<Value, RuntimeError>> {
475 let CallableRequest {
476 identity,
477 fallback_policy,
478 args,
479 requested_outputs,
480 } = request;
481 if let CallableIdentity::BoundFunction(function) = identity {
482 return try_call_semantic_function(function.0, &args, requested_outputs).await;
483 }
484 if !fallback_policy.allows_semantic_name_resolution_for(&identity) {
485 return None;
486 }
487 let name = fallback_policy.resolution_name_for(&identity)?;
488 if matches!(identity, CallableIdentity::DynamicName(_))
489 && crate::class_registry::get_class(&name).is_some()
490 {
491 return None;
494 }
495 if let Some(result) = try_call_semantic_function_by_name(&name, &args, requested_outputs).await
496 {
497 return Some(result);
498 }
499 if matches!(
500 identity,
501 CallableIdentity::DynamicName(_)
502 | CallableIdentity::Imported(_)
503 | CallableIdentity::ExternalName(_)
504 ) {
505 return try_load_and_call_dynamic_function(name, args, requested_outputs).await;
506 }
507 None
508}
509
510#[cfg(test)]
511mod lexical_tests {
512 use super::*;
513 use crate::call::lexical::{LexicalCall, LexicalCallResult, LexicalCapture};
514
515 #[test]
516 fn lexical_invoker_preserves_binding_identity_and_restores_scope() {
517 assert!(current_lexical_function_invoker().is_none());
518 let guard = install_lexical_function_invoker(Some(Arc::new(|mut call| {
519 Box::pin(async move {
520 assert_eq!(call.function, 7);
521 assert_eq!(call.arguments, vec![Value::Num(2.0)]);
522 call.captures[0].value = Value::Num(5.0);
523 Ok(LexicalCallResult {
524 value: Value::Num(7.0),
525 captures: call.captures,
526 })
527 })
528 })));
529 let result = futures::executor::block_on(try_call_lexical_function(LexicalCall {
530 function: 7,
531 captures: vec![LexicalCapture {
532 binding: runmat_types::BindingId(3),
533 value: Value::Num(1.0),
534 }],
535 arguments: vec![Value::Num(2.0)],
536 requested_outputs: 1,
537 }))
538 .expect("lexical invoker is installed")
539 .expect("lexical call succeeds");
540 assert_eq!(result.value, Value::Num(7.0));
541 assert_eq!(result.captures[0].value, Value::Num(5.0));
542 drop(guard);
543 assert!(current_lexical_function_invoker().is_none());
544 }
545
546 #[test]
547 fn external_invoker_preserves_identity_kind_and_restores_scope() {
548 assert!(current_external_function_invoker().is_none());
549 let guard = install_external_function_invoker(Some(Arc::new(|call| {
550 Box::pin(async move {
551 assert_eq!(call.function, 0);
552 assert_eq!(call.display_name, "published");
553 assert_eq!(call.arguments, vec![Value::Num(2.0)]);
554 assert_eq!(call.requested_outputs, 1);
555 Ok(Value::Num(12.0))
556 })
557 })));
558 let result =
559 futures::executor::block_on(try_call_external_function(ExternalFunctionCall {
560 function: 0,
561 display_name: "published".into(),
562 arguments: vec![Value::Num(2.0)],
563 requested_outputs: 1,
564 }))
565 .expect("external invoker is installed")
566 .expect("external call succeeds");
567 assert_eq!(result, Value::Num(12.0));
568 drop(guard);
569 assert!(current_external_function_invoker().is_none());
570 }
571}