1use crate::bytecode::ArgSpec;
2use crate::bytecode::Instr;
3use crate::call::builtins as call_builtins;
4use crate::call::builtins::ImportedBuiltinResolution;
5use crate::call::closures as call_closures;
6use crate::call::feval::FevalDispatch;
7use crate::call::shared::{
8 build_expanded_args_from_specs, collect_multi_outputs, expand_cell_indices,
9 lookup_user_function, prepare_user_call, subsref_brace_numeric_index_values,
10 subsref_empty_brace_cell, validate_user_function_arity, PreparedUserCall,
11};
12use crate::functions::UserFunction;
13use crate::interpreter::debug;
14use crate::interpreter::dispatch::exceptions::{redirect_exception_to_catch, ExceptionHandling};
15use crate::object::class_def as obj_class_def;
16use crate::object::resolve as obj_resolve;
17use runmat_builtins::{MException, Value};
18use runmat_runtime::RuntimeError;
19use std::future::Future;
20
21pub enum FevalHandling {
22 Completed,
23 InvokeUser {
24 name: String,
25 args: Vec<Value>,
26 functions: std::collections::HashMap<String, crate::functions::UserFunction>,
27 },
28}
29
30pub struct PreparedUserDispatch {
31 pub func: UserFunction,
32 pub var_map: std::collections::HashMap<runmat_hir::VarId, runmat_hir::VarId>,
33 pub func_program: runmat_hir::HirProgram,
34 pub func_vars: Vec<Value>,
35}
36
37pub enum BuiltinHandling {
38 Completed,
39 Caught,
40 Uncaught(Box<RuntimeError>),
41}
42
43pub enum MethodHandling {
44 Completed,
45}
46
47pub enum UserCallHandling {
48 Completed,
49 Caught,
50 Uncaught(Box<RuntimeError>),
51}
52
53pub struct ExceptionRouteContext<'a> {
54 pub try_stack: &'a mut Vec<(usize, Option<usize>)>,
55 pub vars: &'a mut Vec<Value>,
56 pub last_exception: &'a mut Option<MException>,
57 pub pc: &'a mut usize,
58}
59
60pub struct BuiltinCallContext<'a> {
61 pub stack: &'a mut Vec<Value>,
62 pub name: &'a str,
63 pub arg_count: usize,
64 pub next_instr: Option<&'a Instr>,
65 pub source_id: Option<runmat_hir::SourceId>,
66 pub call_arg_spans: Option<Vec<runmat_hir::Span>>,
67 pub imports: &'a [(Vec<String>, bool)],
68 pub call_counts: &'a [(usize, usize)],
69 pub exception: ExceptionRouteContext<'a>,
70}
71
72pub struct UserCallContext<'a> {
73 pub stack: &'a mut Vec<Value>,
74 pub name: &'a str,
75 pub out_count: usize,
76 pub bytecode_functions: &'a std::collections::HashMap<String, UserFunction>,
77 pub caller_functions: &'a mut std::collections::HashMap<String, UserFunction>,
78 pub exception: ExceptionRouteContext<'a>,
79}
80
81#[cfg(feature = "native-accel")]
82async fn accel_prepare_args(name: &str, args: &[Value]) -> Result<Vec<Value>, RuntimeError> {
83 Ok(runmat_accelerate::prepare_builtin_args(name, args)
84 .await
85 .map_err(|e| e.to_string())?)
86}
87
88#[cfg(not(feature = "native-accel"))]
89async fn accel_prepare_args(_name: &str, args: &[Value]) -> Result<Vec<Value>, RuntimeError> {
90 Ok(args.to_vec())
91}
92
93async fn call_builtin_auto(name: &str, args: &[Value]) -> Result<Value, RuntimeError> {
94 let prepared = accel_prepare_args(name, args).await?;
95 runmat_runtime::call_builtin_async(name, &prepared).await
96}
97
98fn output_hint_for_next(next_instr: Option<&Instr>) -> usize {
99 match next_instr {
100 Some(Instr::Pop) | Some(Instr::EmitStackTop { .. }) => 0,
101 _ => 1,
102 }
103}
104
105fn requested_output_count_from_next(next_instr: Option<&Instr>) -> Option<usize> {
106 match next_instr {
107 Some(Instr::Unpack(count)) => Some(*count),
108 _ => None,
109 }
110}
111
112pub fn handle_feval_dispatch(
113 dispatch: Result<FevalDispatch, RuntimeError>,
114 stack: &mut Vec<Value>,
115) -> Result<FevalHandling, RuntimeError> {
116 match dispatch? {
117 FevalDispatch::Completed(result) => {
118 stack.push(result);
119 Ok(FevalHandling::Completed)
120 }
121 FevalDispatch::InvokeUser {
122 name,
123 args,
124 functions,
125 } => Ok(FevalHandling::InvokeUser {
126 name,
127 args,
128 functions,
129 }),
130 }
131}
132
133pub fn unpack_prepared_user_call(prepared: PreparedUserCall) -> PreparedUserDispatch {
134 let PreparedUserCall {
135 func,
136 var_map,
137 func_program,
138 func_vars,
139 } = prepared;
140 PreparedUserDispatch {
141 func,
142 var_map,
143 func_program,
144 func_vars,
145 }
146}
147
148pub fn prepare_named_user_dispatch(
149 name: &str,
150 functions: &std::collections::HashMap<String, UserFunction>,
151 args: &[Value],
152 vars: &[Value],
153) -> Result<PreparedUserDispatch, RuntimeError> {
154 let func = lookup_user_function(name, functions)?;
155 validate_user_function_arity(name, &func, args.len())?;
156 let prepared = prepare_user_call(func, args, vars)?;
157 Ok(unpack_prepared_user_call(prepared))
158}
159
160pub fn push_user_call_outputs(
161 stack: &mut Vec<Value>,
162 name: &str,
163 func: &UserFunction,
164 var_map: &std::collections::HashMap<runmat_hir::VarId, runmat_hir::VarId>,
165 func_result_vars: &[Value],
166 out_count: usize,
167) -> Result<(), RuntimeError> {
168 let outputs = collect_multi_outputs(name, func, var_map, func_result_vars, out_count)?;
169 for value in outputs {
170 stack.push(value);
171 }
172 Ok(())
173}
174
175pub fn output_list_for_user_call(
176 name: &str,
177 func: &UserFunction,
178 var_map: &std::collections::HashMap<runmat_hir::VarId, runmat_hir::VarId>,
179 func_result_vars: &[Value],
180 out_count: usize,
181) -> Result<Value, RuntimeError> {
182 let outputs = collect_multi_outputs(name, func, var_map, func_result_vars, out_count)?;
183 Ok(Value::OutputList(outputs))
184}
185
186pub fn push_single_result(stack: &mut Vec<Value>, result: Value) {
187 stack.push(result);
188}
189
190pub async fn build_builtin_expand_last_args<F, Fut>(
191 stack: &mut Vec<Value>,
192 fixed_argc: usize,
193 num_indices: usize,
194 invalid_expand_msg: &'static str,
195 mut expand_object_indices: F,
196) -> Result<Vec<Value>, RuntimeError>
197where
198 F: FnMut(Value, Vec<Value>) -> Fut,
199 Fut: Future<Output = Result<Vec<Value>, RuntimeError>>,
200{
201 let mut indices = Vec::with_capacity(num_indices);
202 for _ in 0..num_indices {
203 indices.push(stack.pop().ok_or(crate::interpreter::errors::mex(
204 "StackUnderflow",
205 "stack underflow",
206 ))?);
207 }
208 indices.reverse();
209 let base = stack.pop().ok_or(crate::interpreter::errors::mex(
210 "StackUnderflow",
211 "stack underflow",
212 ))?;
213 let mut fixed = Vec::with_capacity(fixed_argc);
214 for _ in 0..fixed_argc {
215 fixed.push(stack.pop().ok_or(crate::interpreter::errors::mex(
216 "StackUnderflow",
217 "stack underflow",
218 ))?);
219 }
220 fixed.reverse();
221
222 let expanded = match (base, indices.len()) {
223 (Value::Cell(ca), 1) | (Value::Cell(ca), 2) => expand_cell_indices(&ca, &indices)?,
224 (other, _) => match other {
225 Value::Object(obj) => expand_object_indices(Value::Object(obj), indices).await?,
226 _ => {
227 return Err(crate::interpreter::errors::mex(
228 "ExpandError",
229 invalid_expand_msg,
230 ))
231 }
232 },
233 };
234
235 let mut args = fixed;
236 args.extend(expanded);
237 Ok(args)
238}
239
240pub async fn build_builtin_expand_at_args<F, Fut>(
241 stack: &mut Vec<Value>,
242 before_count: usize,
243 num_indices: usize,
244 after_count: usize,
245 invalid_expand_msg: &'static str,
246 mut expand_object_indices: F,
247) -> Result<Vec<Value>, RuntimeError>
248where
249 F: FnMut(Value, Vec<Value>) -> Fut,
250 Fut: Future<Output = Result<Vec<Value>, RuntimeError>>,
251{
252 let mut after = Vec::with_capacity(after_count);
253 for _ in 0..after_count {
254 after.push(stack.pop().ok_or(crate::interpreter::errors::mex(
255 "StackUnderflow",
256 "stack underflow",
257 ))?);
258 }
259 after.reverse();
260
261 let mut indices = Vec::with_capacity(num_indices);
262 for _ in 0..num_indices {
263 indices.push(stack.pop().ok_or(crate::interpreter::errors::mex(
264 "StackUnderflow",
265 "stack underflow",
266 ))?);
267 }
268 indices.reverse();
269
270 let base = stack.pop().ok_or(crate::interpreter::errors::mex(
271 "StackUnderflow",
272 "stack underflow",
273 ))?;
274
275 let mut before = Vec::with_capacity(before_count);
276 for _ in 0..before_count {
277 before.push(stack.pop().ok_or(crate::interpreter::errors::mex(
278 "StackUnderflow",
279 "stack underflow",
280 ))?);
281 }
282 before.reverse();
283
284 let expanded = match (base, indices.len()) {
285 (Value::Cell(ca), 1) | (Value::Cell(ca), 2) => expand_cell_indices(&ca, &indices)?,
286 (Value::Object(obj), _) => expand_object_indices(Value::Object(obj), indices).await?,
287 _ => {
288 return Err(crate::interpreter::errors::mex(
289 "ExpandError",
290 invalid_expand_msg,
291 ))
292 }
293 };
294
295 let mut args = before;
296 args.extend(expanded);
297 args.extend(after);
298 Ok(args)
299}
300
301pub async fn build_builtin_expand_multi_args(
302 stack: &mut Vec<Value>,
303 specs: &[ArgSpec],
304) -> Result<Vec<Value>, RuntimeError> {
305 build_expanded_args_from_specs(
306 stack,
307 specs,
308 "CallBuiltinExpandMulti requires cell or object for expand_all",
309 "CallBuiltinExpandMulti requires cell or object cell access",
310 |base| async move {
311 match base {
312 Value::Object(obj) => {
313 let empty = subsref_empty_brace_cell()?;
314 let args = vec![
315 Value::Object(obj),
316 Value::String("subsref".to_string()),
317 Value::String("{}".to_string()),
318 empty,
319 ];
320 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
321 Ok(match v {
322 Value::Cell(ca) => crate::call::shared::expand_all_cell(&ca),
323 other => vec![other],
324 })
325 }
326 _ => Err(crate::interpreter::errors::mex(
327 "ExpandError",
328 "CallBuiltinExpandMulti requires cell or object for expand_all",
329 )),
330 }
331 },
332 |base, indices| async move {
333 match base {
334 Value::Object(obj) => {
335 let idx_vals = subsref_brace_numeric_index_values(&indices);
336 let cell = runmat_runtime::call_builtin_async("__make_cell", &idx_vals).await?;
337 let args = vec![
338 Value::Object(obj),
339 Value::String("subsref".to_string()),
340 Value::String("{}".to_string()),
341 cell,
342 ];
343 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
344 Ok(vec![v])
345 }
346 _ => Err(crate::interpreter::errors::mex(
347 "ExpandError",
348 "CallBuiltinExpandMulti requires cell or object cell access",
349 )),
350 }
351 },
352 )
353 .await
354}
355
356pub async fn build_feval_expand_multi_args(
357 stack: &mut Vec<Value>,
358 specs: &[ArgSpec],
359) -> Result<Vec<Value>, RuntimeError> {
360 build_expanded_args_from_specs(
361 stack,
362 specs,
363 "CallFevalExpandMulti requires cell or object for expand_all",
364 "CallFevalExpandMulti requires cell or object cell access",
365 |base| async move {
366 match base {
367 Value::Object(obj) => {
368 let empty = subsref_empty_brace_cell()?;
369 let args = vec![
370 Value::Object(obj),
371 Value::String("subsref".to_string()),
372 Value::String("{}".to_string()),
373 empty,
374 ];
375 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
376 Ok(match v {
377 Value::Cell(ca) => crate::call::shared::expand_all_cell(&ca),
378 other => vec![other],
379 })
380 }
381 _ => Err(crate::interpreter::errors::mex(
382 "InvalidExpandAllTarget",
383 "CallFevalExpandMulti requires cell or object for expand_all",
384 )),
385 }
386 },
387 |base, indices| async move {
388 match base {
389 Value::Object(obj) => {
390 let cell = crate::call::shared::subsref_brace_index_cell_raw(&indices)?;
391 let args = vec![
392 Value::Object(obj),
393 Value::String("subsref".to_string()),
394 Value::String("{}".to_string()),
395 cell,
396 ];
397 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
398 Ok(vec![v])
399 }
400 _ => Err(crate::interpreter::errors::mex(
401 "ExpandError",
402 "CallFevalExpandMulti requires cell or object cell access",
403 )),
404 }
405 },
406 )
407 .await
408}
409
410pub async fn build_user_function_expand_multi_args(
411 stack: &mut Vec<Value>,
412 specs: &[ArgSpec],
413) -> Result<Vec<Value>, RuntimeError> {
414 build_expanded_args_from_specs(
415 stack,
416 specs,
417 "CallFunctionExpandMulti requires cell or object for expand_all",
418 "CallFunctionExpandMulti requires cell or object cell access",
419 |base| async move {
420 match base {
421 Value::Cell(ca) => Ok(crate::call::shared::expand_all_cell(&ca)),
422 Value::Object(obj) => {
423 let empty = subsref_empty_brace_cell()?;
424 let args = vec![
425 Value::Object(obj),
426 Value::String("subsref".to_string()),
427 Value::String("{}".to_string()),
428 empty,
429 ];
430 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
431 Ok(match v {
432 Value::Cell(ca) => crate::call::shared::expand_all_cell(&ca),
433 other => vec![other],
434 })
435 }
436 _ => Err(crate::interpreter::errors::mex(
437 "InvalidExpandAllTarget",
438 "CallFunctionExpandMulti requires cell or object for expand_all",
439 )),
440 }
441 },
442 |base, indices| async move {
443 match (base, indices.len()) {
444 (Value::Cell(ca), 1) | (Value::Cell(ca), 2) => expand_cell_indices(&ca, &indices),
445 (Value::Object(obj), _) => {
446 let cell = crate::call::shared::subsref_brace_index_cell_raw(&indices)?;
447 let args = vec![
448 Value::Object(obj),
449 Value::String("subsref".to_string()),
450 Value::String("{}".to_string()),
451 cell,
452 ];
453 let v = runmat_runtime::call_builtin_async("call_method", &args).await?;
454 Ok(vec![v])
455 }
456 _ => Err(crate::interpreter::errors::mex(
457 "ExpandError",
458 "CallFunctionExpandMulti requires cell or object cell access",
459 )),
460 }
461 },
462 )
463 .await
464}
465
466pub fn handle_builtin_outcome(
467 result: Result<Value, RuntimeError>,
468 imported: ImportedBuiltinResolution,
469 stack: &mut Vec<Value>,
470 ctx: ExceptionRouteContext<'_>,
471 refresh_vars: impl Fn(&[Value]),
472) -> Result<BuiltinHandling, RuntimeError> {
473 let ExceptionRouteContext {
474 try_stack,
475 vars,
476 last_exception,
477 pc,
478 } = ctx;
479 match result {
480 Ok(result) => {
481 stack.push(result);
482 Ok(BuiltinHandling::Completed)
483 }
484 Err(err) => match imported {
485 ImportedBuiltinResolution::Resolved(value) => {
486 stack.push(value);
487 Ok(BuiltinHandling::Completed)
488 }
489 ImportedBuiltinResolution::Ambiguous(message) => Err(message.into()),
490 ImportedBuiltinResolution::NotFound => Ok(
491 match redirect_exception_to_catch(
492 err,
493 try_stack,
494 vars,
495 last_exception,
496 pc,
497 refresh_vars,
498 ) {
499 ExceptionHandling::Caught => BuiltinHandling::Caught,
500 ExceptionHandling::Uncaught(err) => BuiltinHandling::Uncaught(err),
501 },
502 ),
503 },
504 }
505}
506
507pub async fn handle_builtin_call(
508 ctx: BuiltinCallContext<'_>,
509 refresh_vars: impl Fn(&[Value]),
510) -> Result<BuiltinHandling, RuntimeError> {
511 let BuiltinCallContext {
512 stack,
513 name,
514 arg_count,
515 next_instr,
516 source_id,
517 call_arg_spans,
518 imports,
519 call_counts,
520 exception,
521 } = ctx;
522 let ExceptionRouteContext {
523 try_stack: _,
524 vars: _,
525 last_exception,
526 pc,
527 } = &exception;
528 debug::trace_call_builtin(**pc, name, arg_count, stack);
529 if let Some(value) = call_builtins::special_counter_builtin(name, arg_count, call_counts)? {
530 stack.push(value);
531 return Ok(BuiltinHandling::Completed);
532 }
533 let requested_outputs = requested_output_count_from_next(next_instr);
534 let args = call_builtins::collect_call_args(stack, arg_count)?;
535
536 let _callsite_guard = runmat_runtime::callsite::push_callsite(source_id, call_arg_spans);
537 let output_hint = output_hint_for_next(next_instr);
538 let _output_guard = runmat_runtime::output_context::push_output_count(output_hint);
539
540 let prepared_primary = call_builtins::prepare_builtin_args(name, &args).await?;
541 let result = match requested_outputs {
542 Some(count) => {
543 runmat_runtime::call_builtin_async_with_outputs(name, &prepared_primary, count).await
544 }
545 None => runmat_runtime::call_builtin_async(name, &prepared_primary).await,
546 };
547 let imported = call_builtins::resolve_imported_builtin(
548 name,
549 imports,
550 &prepared_primary,
551 requested_outputs,
552 )
553 .await?;
554 if result.is_err() {
555 if let Some(err) = call_builtins::rethrow_without_explicit_exception(
556 name,
557 &args,
558 last_exception.as_ref().map(|e| e.identifier.as_str()),
559 last_exception.as_ref().map(|e| e.message.as_str()),
560 ) {
561 return Err(err);
562 }
563 }
564 handle_builtin_outcome(result, imported, stack, exception, refresh_vars)
565}
566
567pub async fn handle_method_call(
568 stack: &mut Vec<Value>,
569 name: &str,
570 arg_count: usize,
571) -> Result<MethodHandling, RuntimeError> {
572 let (base, args) = call_closures::collect_method_args(stack, arg_count)?;
573 let value = call_closures::call_method(base, name, args).await?;
574 stack.push(value);
575 Ok(MethodHandling::Completed)
576}
577
578pub async fn handle_prepared_user_function_call<BF, BFFut, IF, IFFut>(
579 ctx: UserCallContext<'_>,
580 args: Vec<Value>,
581 refresh_vars: impl Fn(&[Value]),
582 builtin_fallback: BF,
583 interpret_counts: IF,
584) -> Result<UserCallHandling, RuntimeError>
585where
586 BF: FnOnce(String, Vec<Value>, usize) -> BFFut,
587 BFFut: Future<Output = Result<Option<Value>, RuntimeError>>,
588 IF: FnOnce(crate::bytecode::Bytecode, Vec<Value>, String, usize, usize) -> IFFut,
589 IFFut: Future<Output = Result<Vec<Value>, RuntimeError>>,
590{
591 let UserCallContext {
592 stack,
593 name,
594 out_count,
595 bytecode_functions,
596 caller_functions,
597 exception,
598 } = ctx;
599 let ExceptionRouteContext {
600 try_stack,
601 vars,
602 last_exception,
603 pc,
604 } = exception;
605 let arg_count = args.len();
606 if let Some(result) = builtin_fallback(name.to_string(), args.clone(), out_count).await? {
607 stack.push(result);
608 return Ok(UserCallHandling::Completed);
609 }
610
611 let prepared = prepare_named_user_dispatch(name, bytecode_functions, &args, vars)?;
612 let PreparedUserDispatch {
613 func,
614 var_map,
615 func_program,
616 func_vars,
617 } = prepared;
618 let mut func_bytecode = crate::compile(&func_program, bytecode_functions)?;
619 func_bytecode.source_id = func.source_id;
620 for (k, v) in func_bytecode.functions.iter() {
621 caller_functions.insert(k.clone(), v.clone());
622 }
623
624 let func_result_vars = match interpret_counts(
625 func_bytecode,
626 func_vars,
627 name.to_string(),
628 out_count,
629 arg_count,
630 )
631 .await
632 {
633 Ok(v) => v,
634 Err(e) => {
635 return Ok(
636 match redirect_exception_to_catch(
637 e,
638 try_stack,
639 vars,
640 last_exception,
641 pc,
642 refresh_vars,
643 ) {
644 ExceptionHandling::Caught => UserCallHandling::Caught,
645 ExceptionHandling::Uncaught(err) => UserCallHandling::Uncaught(err),
646 },
647 )
648 }
649 };
650
651 if out_count == 1 {
652 push_user_call_outputs(stack, name, &func, &var_map, &func_result_vars, 1)?;
653 } else {
654 let output_list =
655 output_list_for_user_call(name, &func, &var_map, &func_result_vars, out_count)?;
656 push_single_result(stack, output_list);
657 }
658 Ok(UserCallHandling::Completed)
659}
660
661pub async fn handle_user_function_call<BF, BFFut, IF, IFFut>(
662 ctx: UserCallContext<'_>,
663 arg_count: usize,
664 refresh_vars: impl Fn(&[Value]),
665 builtin_fallback: BF,
666 interpret_counts: IF,
667) -> Result<UserCallHandling, RuntimeError>
668where
669 BF: FnOnce(String, Vec<Value>, usize) -> BFFut,
670 BFFut: Future<Output = Result<Option<Value>, RuntimeError>>,
671 IF: FnOnce(crate::bytecode::Bytecode, Vec<Value>, String, usize, usize) -> IFFut,
672 IFFut: Future<Output = Result<Vec<Value>, RuntimeError>>,
673{
674 let args = crate::call::builtins::collect_call_args(ctx.stack, arg_count)?;
675 handle_prepared_user_function_call(ctx, args, refresh_vars, builtin_fallback, interpret_counts)
676 .await
677}
678
679pub async fn handle_builtin_expand_last_call<F, Fut>(
680 stack: &mut Vec<Value>,
681 name: &str,
682 fixed_argc: usize,
683 num_indices: usize,
684 next_instr: Option<&Instr>,
685 expand_object_indices: F,
686) -> Result<BuiltinHandling, RuntimeError>
687where
688 F: FnMut(Value, Vec<Value>) -> Fut,
689 Fut: Future<Output = Result<Vec<Value>, RuntimeError>>,
690{
691 let args = build_builtin_expand_last_args(
692 stack,
693 fixed_argc,
694 num_indices,
695 "CallBuiltinExpandLast requires cell or object cell access",
696 expand_object_indices,
697 )
698 .await?;
699 let output_hint = output_hint_for_next(next_instr);
700 let _output_guard = runmat_runtime::output_context::push_output_count(output_hint);
701 push_single_result(stack, call_builtin_auto(name, &args).await?);
702 Ok(BuiltinHandling::Completed)
703}
704
705pub async fn handle_builtin_expand_at_call<F, Fut>(
706 stack: &mut Vec<Value>,
707 name: &str,
708 before_count: usize,
709 num_indices: usize,
710 after_count: usize,
711 next_instr: Option<&Instr>,
712 expand_object_indices: F,
713) -> Result<BuiltinHandling, RuntimeError>
714where
715 F: FnMut(Value, Vec<Value>) -> Fut,
716 Fut: Future<Output = Result<Vec<Value>, RuntimeError>>,
717{
718 let args = build_builtin_expand_at_args(
719 stack,
720 before_count,
721 num_indices,
722 after_count,
723 "CallBuiltinExpandAt requires cell or object cell access",
724 expand_object_indices,
725 )
726 .await?;
727 let output_hint = output_hint_for_next(next_instr);
728 let _output_guard = runmat_runtime::output_context::push_output_count(output_hint);
729 push_single_result(stack, call_builtin_auto(name, &args).await?);
730 Ok(BuiltinHandling::Completed)
731}
732
733pub async fn handle_builtin_expand_multi_call(
734 stack: &mut Vec<Value>,
735 name: &str,
736 specs: &[ArgSpec],
737 next_instr: Option<&Instr>,
738) -> Result<BuiltinHandling, RuntimeError> {
739 let args = build_builtin_expand_multi_args(stack, specs).await?;
740 let output_hint = output_hint_for_next(next_instr);
741 let _output_guard = runmat_runtime::output_context::push_output_count(output_hint);
742 push_single_result(stack, call_builtin_auto(name, &args).await?);
743 Ok(BuiltinHandling::Completed)
744}
745
746pub async fn handle_method_or_member_index_call(
747 stack: &mut Vec<Value>,
748 name: String,
749 arg_count: usize,
750) -> Result<MethodHandling, RuntimeError> {
751 let (base, args) = call_closures::collect_method_args(stack, arg_count)?;
752 let value = call_closures::call_method_or_member_index(base, name, args).await?;
753 stack.push(value);
754 Ok(MethodHandling::Completed)
755}
756
757pub async fn handle_static_method_call(
758 stack: &mut Vec<Value>,
759 class_name: &str,
760 method: &str,
761 arg_count: usize,
762) -> Result<MethodHandling, RuntimeError> {
763 let mut args = crate::call::builtins::collect_call_args(stack, arg_count)?;
764 match call_closures::call_static_method(class_name, method, args.clone()).await {
765 Ok(v) => {
766 stack.push(v);
767 Ok(MethodHandling::Completed)
768 }
769 Err(_) => {
770 let is_type_class = matches!(
771 class_name,
772 "gpuArray"
773 | "logical"
774 | "double"
775 | "single"
776 | "int8"
777 | "int16"
778 | "int32"
779 | "int64"
780 | "uint8"
781 | "uint16"
782 | "uint32"
783 | "uint64"
784 | "char"
785 | "string"
786 | "cell"
787 | "struct"
788 );
789 if is_type_class {
790 args.push(Value::from(class_name));
791 let v = runmat_runtime::call_builtin_async(method, &args).await?;
792 stack.push(v);
793 Ok(MethodHandling::Completed)
794 } else {
795 Err(format!("Unknown static method '{}' on class {}", method, class_name).into())
796 }
797 }
798 }
799}
800
801pub fn handle_load_method(
802 stack: &mut Vec<Value>,
803 name: String,
804) -> Result<MethodHandling, RuntimeError> {
805 let base = crate::interpreter::stack::pop_value(stack)?;
806 let value = call_closures::load_method_closure(base, name)?;
807 stack.push(value);
808 Ok(MethodHandling::Completed)
809}
810
811pub fn handle_create_closure(
812 stack: &mut Vec<Value>,
813 func_name: String,
814 capture_count: usize,
815) -> Result<MethodHandling, RuntimeError> {
816 call_closures::create_closure(stack, func_name, capture_count)?;
817 Ok(MethodHandling::Completed)
818}
819
820pub fn handle_load_static_property(
821 stack: &mut Vec<Value>,
822 class_name: &str,
823 prop: &str,
824) -> Result<MethodHandling, RuntimeError> {
825 let value = obj_resolve::load_static_member(class_name, prop)?;
826 stack.push(value);
827 Ok(MethodHandling::Completed)
828}
829
830pub fn handle_register_class(
831 name: String,
832 super_class: Option<String>,
833 properties: Vec<(String, bool, String, String)>,
834 methods: Vec<(String, String, bool, String)>,
835) -> Result<MethodHandling, RuntimeError> {
836 obj_class_def::register_class(name, super_class, properties, methods)?;
837 Ok(MethodHandling::Completed)
838}