1mod arithmetic;
2mod arrays;
3mod calls;
4mod control_flow;
5mod exceptions;
6mod indexing;
7mod object;
8mod stack;
9
10use crate::bytecode::Instr;
11use crate::interpreter::debug;
12use crate::runtime::workspace::refresh_workspace_state;
13use runmat_builtins::Value;
14use runmat_runtime::dispatcher::gather_if_needed_async;
15use runmat_runtime::RuntimeError;
16use std::collections::HashMap;
17use std::future::Future;
18use std::pin::Pin;
19
20pub use arrays::{
21 create_matrix, create_matrix_dynamic, create_range, pack_to_col, pack_to_row, unpack,
22};
23pub use calls::{
24 build_builtin_expand_at_args, build_builtin_expand_last_args, build_builtin_expand_multi_args,
25 build_feval_expand_multi_args, build_user_function_expand_multi_args, handle_builtin_call,
26 handle_builtin_expand_at_call, handle_builtin_expand_last_call,
27 handle_builtin_expand_multi_call, handle_builtin_outcome, handle_create_closure,
28 handle_feval_dispatch, handle_load_method, handle_load_static_property, handle_method_call,
29 handle_method_or_member_index_call, handle_prepared_user_function_call, handle_register_class,
30 handle_static_method_call, handle_user_function_call, output_list_for_user_call,
31 prepare_named_user_dispatch, push_single_result, push_user_call_outputs,
32 unpack_prepared_user_call, BuiltinHandling, FevalHandling, MethodHandling,
33 PreparedUserDispatch, UserCallHandling,
34};
35pub use control_flow::{apply_control_flow_action, DispatchDecision};
36pub use exceptions::{
37 parse_exception, prepare_vm_error, redirect_exception_to_catch, ExceptionHandling,
38};
39pub use stack::{
40 emit_stack_top, emit_var, load_bool, load_char_row, load_complex, load_const, load_local,
41 load_string, load_var, store_local, store_var,
42};
43
44pub enum DispatchHandled {
45 Generic(DispatchDecision),
46 ReturnValue(DispatchDecision),
47 Return(DispatchDecision),
48}
49
50pub type InvokeUserForEndExpr<'a> = dyn for<'b> Fn(
51 &'b str,
52 Vec<Value>,
53 &'b HashMap<String, crate::bytecode::UserFunction>,
54 &'b [Value],
55 ) -> Pin<Box<dyn Future<Output = Result<Value, RuntimeError>> + 'b>>
56 + 'a;
57
58pub type BuiltinFallbackUserCall<'a> = dyn Fn(
59 String,
60 Vec<Value>,
61 usize,
62 ) -> Pin<Box<dyn Future<Output = Result<Option<Value>, RuntimeError>>>>
63 + 'a;
64
65pub type InterpretFunctionCounts<'a> = dyn Fn(
66 crate::bytecode::Bytecode,
67 Vec<Value>,
68 String,
69 usize,
70 usize,
71 ) -> Pin<Box<dyn Future<Output = Result<Vec<Value>, RuntimeError>>>>
72 + 'a;
73
74pub struct DispatchMeta<'a> {
75 pub instr: &'a Instr,
76 pub var_names: &'a HashMap<usize, String>,
77 pub bytecode_functions: &'a HashMap<String, crate::bytecode::UserFunction>,
78 pub source_id: Option<runmat_hir::SourceId>,
79 pub call_arg_spans: Option<Vec<runmat_hir::Span>>,
80 pub call_counts: &'a [(usize, usize)],
81 pub current_function_name: &'a str,
82 pub next_instr: Option<&'a Instr>,
83}
84
85pub struct DispatchState<'a> {
86 pub stack: &'a mut Vec<Value>,
87 pub vars: &'a mut Vec<Value>,
88 pub context: &'a mut crate::bytecode::ExecutionContext,
89 pub try_stack: &'a mut Vec<(usize, Option<usize>)>,
90 pub last_exception: &'a mut Option<runmat_builtins::MException>,
91 pub imports: &'a mut Vec<(Vec<String>, bool)>,
92 pub global_aliases: &'a mut HashMap<usize, String>,
93 pub persistent_aliases: &'a mut HashMap<usize, String>,
94 pub pc: &'a mut usize,
95}
96
97pub struct DispatchHooks<'a> {
98 pub clear_value_residency: &'a mut dyn FnMut(&Value),
99 pub invoke_user_for_end_expr: &'a InvokeUserForEndExpr<'a>,
100 pub builtin_fallback_user_call: &'a BuiltinFallbackUserCall<'a>,
101 pub interpret_function_counts: &'a InterpretFunctionCounts<'a>,
102 pub store_var_before_overwrite: &'a mut dyn FnMut(&Value, &Value),
103 pub store_var_after_store: &'a mut dyn FnMut(usize, &Value),
104 pub store_local_before_local_overwrite: &'a mut dyn FnMut(&Value, &Value),
105 pub store_local_before_var_overwrite: &'a mut dyn FnMut(&Value, &Value),
106 pub store_local_after_fallback_store: &'a mut dyn FnMut(&str, usize, &Value),
107}
108
109pub async fn logical_truth_from_value(value: &Value, label: &str) -> Result<bool, RuntimeError> {
110 match value {
111 Value::Bool(flag) => Ok(*flag),
112 Value::Int(i) => Ok(!i.is_zero()),
113 Value::Num(n) => Ok(*n != 0.0),
114 Value::LogicalArray(array) if array.data.len() == 1 => Ok(array.data[0] != 0),
115 Value::LogicalArray(array) => Err(crate::interpreter::errors::mex(
116 "InvalidConditionType",
117 &format!(
118 "{label}: expected scalar logical or numeric value, got logical array with {} elements",
119 array.data.len()
120 ),
121 )),
122 Value::Tensor(tensor) if tensor.data.len() == 1 => Ok(tensor.data[0] != 0.0),
123 Value::Tensor(tensor) => Err(crate::interpreter::errors::mex(
124 "InvalidConditionType",
125 &format!(
126 "{label}: expected scalar logical or numeric value, got numeric array with {} elements",
127 tensor.data.len()
128 ),
129 )),
130 Value::GpuTensor(_) => {
131 let gathered = gather_if_needed_async(value)
132 .await
133 .map_err(|e| format!("{label}: {e}"))?;
134 Box::pin(logical_truth_from_value(&gathered, label)).await
135 }
136 other => Err(crate::interpreter::errors::mex(
137 "InvalidConditionType",
138 &format!("{label}: expected scalar logical or numeric value, got {other:?}"),
139 )),
140 }
141}
142
143pub async fn dispatch_instruction(
144 meta: DispatchMeta<'_>,
145 state: DispatchState<'_>,
146 hooks: DispatchHooks<'_>,
147) -> Result<Option<DispatchHandled>, RuntimeError> {
148 let DispatchMeta {
149 instr,
150 var_names,
151 bytecode_functions,
152 source_id,
153 call_arg_spans,
154 call_counts,
155 current_function_name,
156 next_instr,
157 } = meta;
158 let DispatchState {
159 stack,
160 vars,
161 context,
162 try_stack,
163 last_exception,
164 imports,
165 global_aliases,
166 persistent_aliases,
167 pc,
168 } = state;
169 let DispatchHooks {
170 clear_value_residency,
171 invoke_user_for_end_expr,
172 builtin_fallback_user_call,
173 interpret_function_counts,
174 store_var_before_overwrite,
175 store_var_after_store,
176 store_local_before_local_overwrite,
177 store_local_before_var_overwrite,
178 store_local_after_fallback_store,
179 } = hooks;
180 match instr {
181 _ if indexing::dispatch_indexing(
182 instr,
183 stack,
184 vars,
185 &context.functions,
186 *pc,
187 &mut *clear_value_residency,
188 &invoke_user_for_end_expr,
189 )
190 .await? =>
191 {
192 Ok(Some(DispatchHandled::Generic(
193 DispatchDecision::FallThrough,
194 )))
195 }
196 _ if object::dispatch_object(instr, stack).await? => Ok(Some(DispatchHandled::Generic(
197 DispatchDecision::FallThrough,
198 ))),
199 _ if arithmetic::dispatch_arithmetic(instr, stack).await? => Ok(Some(
200 DispatchHandled::Generic(DispatchDecision::FallThrough),
201 )),
202 Instr::EmitStackTop { label } => {
203 emit_stack_top(stack, label, var_names).await?;
204 Ok(Some(DispatchHandled::Generic(
205 DispatchDecision::FallThrough,
206 )))
207 }
208 Instr::EmitVar { var_index, label } => {
209 emit_var(vars, *var_index, label, var_names).await?;
210 Ok(Some(DispatchHandled::Generic(
211 DispatchDecision::FallThrough,
212 )))
213 }
214 Instr::LoadConst(value) => {
215 load_const(stack, *value);
216 Ok(Some(DispatchHandled::Generic(
217 DispatchDecision::FallThrough,
218 )))
219 }
220 Instr::LoadComplex(re, im) => {
221 load_complex(stack, *re, *im);
222 Ok(Some(DispatchHandled::Generic(
223 DispatchDecision::FallThrough,
224 )))
225 }
226 Instr::LoadBool(value) => {
227 load_bool(stack, *value);
228 Ok(Some(DispatchHandled::Generic(
229 DispatchDecision::FallThrough,
230 )))
231 }
232 Instr::LoadString(value) => {
233 load_string(stack, value.clone());
234 Ok(Some(DispatchHandled::Generic(
235 DispatchDecision::FallThrough,
236 )))
237 }
238 Instr::LoadCharRow(value) => {
239 load_char_row(stack, value.clone())?;
240 Ok(Some(DispatchHandled::Generic(
241 DispatchDecision::FallThrough,
242 )))
243 }
244 Instr::LoadLocal(offset) => {
245 load_local(stack, context, vars, *offset)?;
246 Ok(Some(DispatchHandled::Generic(
247 DispatchDecision::FallThrough,
248 )))
249 }
250 Instr::LoadVar(index) => {
251 let value = vars[*index].clone();
252 debug::trace_load_var(*pc, *index, &value);
253 load_var(stack, vars, *index);
254 Ok(Some(DispatchHandled::Generic(
255 DispatchDecision::FallThrough,
256 )))
257 }
258 Instr::StoreVar(index) => {
259 let preview = stack
260 .last()
261 .cloned()
262 .ok_or(crate::interpreter::errors::mex(
263 "StackUnderflow",
264 "stack underflow",
265 ))?;
266 debug::trace_store_var(*pc, *index, &preview);
267 store_var(
268 stack,
269 vars,
270 *index,
271 var_names,
272 store_var_before_overwrite,
273 store_var_after_store,
274 )?;
275 Ok(Some(DispatchHandled::Generic(
276 DispatchDecision::FallThrough,
277 )))
278 }
279 Instr::StoreLocal(offset) => {
280 store_local(
281 stack,
282 context,
283 vars,
284 *offset,
285 store_local_before_local_overwrite,
286 store_local_before_var_overwrite,
287 store_local_after_fallback_store,
288 )?;
289 Ok(Some(DispatchHandled::Generic(
290 DispatchDecision::FallThrough,
291 )))
292 }
293 Instr::Swap => {
294 crate::ops::stack::swap(stack)?;
295 Ok(Some(DispatchHandled::Generic(
296 DispatchDecision::FallThrough,
297 )))
298 }
299 Instr::Pop => {
300 crate::ops::stack::pop(stack);
301 Ok(Some(DispatchHandled::Generic(
302 DispatchDecision::FallThrough,
303 )))
304 }
305 Instr::AndAnd(target) => Ok(Some(DispatchHandled::Generic(apply_control_flow_action(
306 crate::ops::control_flow::and_and(stack, *target)?,
307 pc,
308 )))),
309 Instr::OrOr(target) => Ok(Some(DispatchHandled::Generic(apply_control_flow_action(
310 crate::ops::control_flow::or_or(stack, *target)?,
311 pc,
312 )))),
313 Instr::JumpIfFalse(target) => {
314 let cond = crate::interpreter::stack::pop_value(stack)?;
315 let truth = logical_truth_from_value(&cond, "if condition").await?;
316 Ok(Some(DispatchHandled::Generic(apply_control_flow_action(
317 crate::ops::control_flow::jump_if_false(truth, *target),
318 pc,
319 ))))
320 }
321 Instr::Jump(target) => Ok(Some(DispatchHandled::Generic(apply_control_flow_action(
322 crate::ops::control_flow::jump(*target),
323 pc,
324 )))),
325 Instr::EnterTry(catch_pc, catch_var) => {
326 crate::ops::control_flow::enter_try(try_stack, *catch_pc, *catch_var);
327 Ok(Some(DispatchHandled::Generic(
328 DispatchDecision::FallThrough,
329 )))
330 }
331 Instr::PopTry => {
332 crate::ops::control_flow::pop_try(try_stack);
333 Ok(Some(DispatchHandled::Generic(
334 DispatchDecision::FallThrough,
335 )))
336 }
337 Instr::ReturnValue => Ok(Some(DispatchHandled::ReturnValue(
338 apply_control_flow_action(crate::ops::control_flow::return_value(stack)?, pc),
339 ))),
340 Instr::Return => Ok(Some(DispatchHandled::Return(apply_control_flow_action(
341 crate::ops::control_flow::return_void(),
342 pc,
343 )))),
344 Instr::EnterScope(local_count) => {
345 crate::ops::control_flow::enter_scope(&mut context.locals, *local_count);
346 Ok(Some(DispatchHandled::Generic(
347 DispatchDecision::FallThrough,
348 )))
349 }
350 Instr::ExitScope(local_count) => {
351 crate::ops::control_flow::exit_scope(&mut context.locals, *local_count, |val| {
352 clear_value_residency(val);
353 });
354 Ok(Some(DispatchHandled::Generic(
355 DispatchDecision::FallThrough,
356 )))
357 }
358 Instr::RegisterImport { path, wildcard } => {
359 imports.push((path.clone(), *wildcard));
360 Ok(Some(DispatchHandled::Generic(
361 DispatchDecision::FallThrough,
362 )))
363 }
364 Instr::DeclareGlobal(indices) => {
365 crate::runtime::globals::declare_global(indices.clone(), vars);
366 Ok(Some(DispatchHandled::Generic(
367 DispatchDecision::FallThrough,
368 )))
369 }
370 Instr::DeclareGlobalNamed(indices, names) => {
371 crate::runtime::globals::declare_global_named(
372 indices.clone(),
373 names.clone(),
374 vars,
375 global_aliases,
376 );
377 Ok(Some(DispatchHandled::Generic(
378 DispatchDecision::FallThrough,
379 )))
380 }
381 Instr::DeclarePersistent(indices) => {
382 crate::runtime::globals::declare_persistent(
383 current_function_name,
384 indices.clone(),
385 vars,
386 );
387 Ok(Some(DispatchHandled::Generic(
388 DispatchDecision::FallThrough,
389 )))
390 }
391 Instr::DeclarePersistentNamed(indices, names) => {
392 crate::runtime::globals::declare_persistent_named(
393 current_function_name,
394 indices.clone(),
395 names.clone(),
396 vars,
397 persistent_aliases,
398 );
399 Ok(Some(DispatchHandled::Generic(
400 DispatchDecision::FallThrough,
401 )))
402 }
403 Instr::PackToRow(count) => {
404 pack_to_row(stack, *count)?;
405 Ok(Some(DispatchHandled::Generic(
406 DispatchDecision::FallThrough,
407 )))
408 }
409 Instr::PackToCol(count) => {
410 pack_to_col(stack, *count)?;
411 Ok(Some(DispatchHandled::Generic(
412 DispatchDecision::FallThrough,
413 )))
414 }
415 Instr::Unpack(out_count) => {
416 if *out_count > 0 {
417 unpack(stack, *out_count)?;
418 }
419 Ok(Some(DispatchHandled::Generic(
420 DispatchDecision::FallThrough,
421 )))
422 }
423 Instr::CreateMatrix(rows, cols) => {
424 create_matrix(stack, *rows, *cols)?;
425 Ok(Some(DispatchHandled::Generic(
426 DispatchDecision::FallThrough,
427 )))
428 }
429 Instr::CreateMatrixDynamic(num_rows) => {
430 create_matrix_dynamic(stack, *num_rows, |rows_data| async move {
431 runmat_runtime::create_matrix_from_values(&rows_data).await
432 })
433 .await?;
434 Ok(Some(DispatchHandled::Generic(
435 DispatchDecision::FallThrough,
436 )))
437 }
438 Instr::CreateRange(has_step) => {
439 create_range(stack, *has_step, |args| async move {
440 runmat_runtime::call_builtin_async("colon", &args).await
441 })
442 .await?;
443 Ok(Some(DispatchHandled::Generic(
444 DispatchDecision::FallThrough,
445 )))
446 }
447 Instr::CreateCell2D(rows, cols) => {
448 let mut elems = Vec::with_capacity(*rows * *cols);
449 for _ in 0..(*rows * *cols) {
450 elems.push(stack.pop().ok_or(crate::interpreter::errors::mex(
451 "StackUnderflow",
452 "stack underflow",
453 ))?);
454 }
455 elems.reverse();
456 stack.push(crate::ops::cells::create_cell_2d(elems, *rows, *cols)?);
457 Ok(Some(DispatchHandled::Generic(
458 DispatchDecision::FallThrough,
459 )))
460 }
461 Instr::CallBuiltin(name, arg_count) => {
462 match handle_builtin_call(
463 calls::BuiltinCallContext {
464 stack,
465 name,
466 arg_count: *arg_count,
467 next_instr,
468 source_id,
469 call_arg_spans,
470 imports,
471 call_counts,
472 exception: calls::ExceptionRouteContext {
473 try_stack,
474 vars,
475 last_exception,
476 pc,
477 },
478 },
479 refresh_workspace_state,
480 )
481 .await?
482 {
483 BuiltinHandling::Completed => {}
484 BuiltinHandling::Caught => {
485 return Ok(Some(DispatchHandled::Generic(
486 DispatchDecision::ContinueLoop,
487 )))
488 }
489 BuiltinHandling::Uncaught(err) => return Err(*err),
490 }
491 Ok(Some(DispatchHandled::Generic(
492 DispatchDecision::FallThrough,
493 )))
494 }
495 Instr::CallFeval(argc) => {
496 let args = crate::call::builtins::collect_call_args(stack, *argc)?;
497 let func_val = crate::interpreter::stack::pop_value(stack)?;
498 match handle_feval_dispatch(
499 crate::call::feval::execute_feval(
500 func_val,
501 args,
502 &context.functions,
503 bytecode_functions,
504 )
505 .await,
506 stack,
507 )? {
508 FevalHandling::Completed => {}
509 FevalHandling::InvokeUser {
510 name,
511 args,
512 functions,
513 } => {
514 let value = invoke_user_for_end_expr(&name, args, &functions, vars).await?;
515 stack.push(value);
516 }
517 }
518 Ok(Some(DispatchHandled::Generic(
519 DispatchDecision::FallThrough,
520 )))
521 }
522 Instr::CallFevalExpandMulti(specs) => {
523 let args = build_feval_expand_multi_args(stack, specs).await?;
524 let func_val = crate::interpreter::stack::pop_value(stack)?;
525 match handle_feval_dispatch(
526 crate::call::feval::execute_feval(
527 func_val,
528 args,
529 &context.functions,
530 bytecode_functions,
531 )
532 .await,
533 stack,
534 )? {
535 FevalHandling::Completed => {}
536 FevalHandling::InvokeUser {
537 name,
538 args,
539 functions,
540 } => {
541 let value = invoke_user_for_end_expr(&name, args, &functions, vars).await?;
542 stack.push(value);
543 }
544 }
545 Ok(Some(DispatchHandled::Generic(
546 DispatchDecision::FallThrough,
547 )))
548 }
549 Instr::CallFunction(name, arg_count) => {
550 match handle_user_function_call(
551 calls::UserCallContext {
552 stack,
553 name,
554 out_count: 1,
555 bytecode_functions,
556 caller_functions: &mut context.functions,
557 exception: calls::ExceptionRouteContext {
558 try_stack,
559 vars,
560 last_exception,
561 pc,
562 },
563 },
564 *arg_count,
565 refresh_workspace_state,
566 builtin_fallback_user_call,
567 |bc, vars, name, out_count, in_count| {
568 interpret_function_counts(bc, vars, name, out_count, in_count)
569 },
570 )
571 .await?
572 {
573 UserCallHandling::Completed => {}
574 UserCallHandling::Caught => {
575 return Ok(Some(DispatchHandled::Generic(
576 DispatchDecision::ContinueLoop,
577 )))
578 }
579 UserCallHandling::Uncaught(err) => return Err(*err),
580 }
581 Ok(Some(DispatchHandled::Generic(
582 DispatchDecision::FallThrough,
583 )))
584 }
585 Instr::CallFunctionMulti(name, arg_count, out_count) => {
586 match handle_user_function_call(
587 calls::UserCallContext {
588 stack,
589 name,
590 out_count: *out_count,
591 bytecode_functions,
592 caller_functions: &mut context.functions,
593 exception: calls::ExceptionRouteContext {
594 try_stack,
595 vars,
596 last_exception,
597 pc,
598 },
599 },
600 *arg_count,
601 refresh_workspace_state,
602 builtin_fallback_user_call,
603 |bc, vars, name, out_count, in_count| {
604 interpret_function_counts(bc, vars, name, out_count, in_count)
605 },
606 )
607 .await?
608 {
609 UserCallHandling::Completed => {}
610 UserCallHandling::Caught => {
611 return Ok(Some(DispatchHandled::Generic(
612 DispatchDecision::ContinueLoop,
613 )))
614 }
615 UserCallHandling::Uncaught(err) => return Err(*err),
616 }
617 Ok(Some(DispatchHandled::Generic(
618 DispatchDecision::FallThrough,
619 )))
620 }
621 Instr::CallBuiltinExpandLast(name, fixed_argc, num_indices) => {
622 handle_builtin_expand_last_call(
623 stack,
624 name,
625 *fixed_argc,
626 *num_indices,
627 next_instr,
628 |base, indices| async move {
629 let obj = match base {
630 Value::Object(obj) => obj,
631 _ => unreachable!(),
632 };
633 let cell = crate::call::shared::subsref_paren_index_cell(&indices)?;
634 let v = runmat_runtime::call_builtin_async(
635 "call_method",
636 &[
637 Value::Object(obj),
638 Value::String("subsref".to_string()),
639 Value::String("()".to_string()),
640 cell,
641 ],
642 )
643 .await?;
644 Ok(vec![v])
645 },
646 )
647 .await?;
648 Ok(Some(DispatchHandled::Generic(
649 DispatchDecision::FallThrough,
650 )))
651 }
652 Instr::CallBuiltinExpandAt(name, before_count, num_indices, after_count) => {
653 handle_builtin_expand_at_call(
654 stack,
655 name,
656 *before_count,
657 *num_indices,
658 *after_count,
659 next_instr,
660 |base, indices| async move {
661 let obj = match base {
662 Value::Object(obj) => obj,
663 _ => unreachable!(),
664 };
665 let idx_vals =
666 crate::call::shared::subsref_brace_numeric_index_values(&indices);
667 let cell = runmat_runtime::call_builtin_async("__make_cell", &idx_vals).await?;
668 let v = runmat_runtime::call_builtin_async(
669 "call_method",
670 &[
671 Value::Object(obj),
672 Value::String("subsref".to_string()),
673 Value::String("{}".to_string()),
674 cell,
675 ],
676 )
677 .await?;
678 Ok(vec![v])
679 },
680 )
681 .await?;
682 Ok(Some(DispatchHandled::Generic(
683 DispatchDecision::FallThrough,
684 )))
685 }
686 Instr::CallBuiltinExpandMulti(name, specs) => {
687 handle_builtin_expand_multi_call(stack, name, specs, next_instr).await?;
688 Ok(Some(DispatchHandled::Generic(
689 DispatchDecision::FallThrough,
690 )))
691 }
692 Instr::CallFunctionExpandAt(name, before_count, num_indices, after_count) => {
693 let args = build_builtin_expand_at_args(
694 stack,
695 *before_count,
696 *num_indices,
697 *after_count,
698 "CallFunctionExpandAt requires cell or object cell access",
699 |base, indices| async move {
700 let obj = match base {
701 Value::Object(obj) => obj,
702 _ => unreachable!(),
703 };
704 let cell = crate::call::shared::subsref_brace_index_cell_raw(&indices)?;
705 let v = runmat_runtime::call_builtin_async(
706 "call_method",
707 &[
708 Value::Object(obj),
709 Value::String("subsref".to_string()),
710 Value::String("{}".to_string()),
711 cell,
712 ],
713 )
714 .await?;
715 Ok(vec![v])
716 },
717 )
718 .await?;
719 let value = invoke_user_for_end_expr(name, args, bytecode_functions, vars).await?;
720 stack.push(value);
721 Ok(Some(DispatchHandled::Generic(
722 DispatchDecision::FallThrough,
723 )))
724 }
725 Instr::CallFunctionExpandMulti(name, specs) => {
726 let args = build_user_function_expand_multi_args(stack, specs).await?;
727 match handle_prepared_user_function_call(
728 calls::UserCallContext {
729 stack,
730 name,
731 out_count: 1,
732 bytecode_functions,
733 caller_functions: &mut context.functions,
734 exception: calls::ExceptionRouteContext {
735 try_stack,
736 vars,
737 last_exception,
738 pc,
739 },
740 },
741 args,
742 refresh_workspace_state,
743 builtin_fallback_user_call,
744 |bc, vars, name, out_count, in_count| {
745 interpret_function_counts(bc, vars, name, out_count, in_count)
746 },
747 )
748 .await?
749 {
750 UserCallHandling::Completed => {}
751 UserCallHandling::Caught => {
752 return Ok(Some(DispatchHandled::Generic(
753 DispatchDecision::ContinueLoop,
754 )))
755 }
756 UserCallHandling::Uncaught(err) => return Err(*err),
757 }
758 Ok(Some(DispatchHandled::Generic(
759 DispatchDecision::FallThrough,
760 )))
761 }
762 Instr::CallMethod(name, arg_count) => {
763 handle_method_call(stack, name, *arg_count).await?;
764 Ok(Some(DispatchHandled::Generic(
765 DispatchDecision::FallThrough,
766 )))
767 }
768 Instr::CallMethodOrMemberIndex(name, arg_count) => {
769 handle_method_or_member_index_call(stack, name.clone(), *arg_count).await?;
770 Ok(Some(DispatchHandled::Generic(
771 DispatchDecision::FallThrough,
772 )))
773 }
774 Instr::LoadMethod(name) => {
775 handle_load_method(stack, name.clone())?;
776 Ok(Some(DispatchHandled::Generic(
777 DispatchDecision::FallThrough,
778 )))
779 }
780 Instr::CreateClosure(func_name, capture_count) => {
781 handle_create_closure(stack, func_name.clone(), *capture_count)?;
782 Ok(Some(DispatchHandled::Generic(
783 DispatchDecision::FallThrough,
784 )))
785 }
786 Instr::LoadStaticProperty(class_name, prop) => {
787 handle_load_static_property(stack, class_name, prop)?;
788 Ok(Some(DispatchHandled::Generic(
789 DispatchDecision::FallThrough,
790 )))
791 }
792 Instr::CallStaticMethod(class_name, method, arg_count) => {
793 handle_static_method_call(stack, class_name, method, *arg_count).await?;
794 Ok(Some(DispatchHandled::Generic(
795 DispatchDecision::FallThrough,
796 )))
797 }
798 Instr::RegisterClass {
799 name,
800 super_class,
801 properties,
802 methods,
803 } => {
804 handle_register_class(
805 name.clone(),
806 super_class.clone(),
807 properties.clone(),
808 methods.clone(),
809 )?;
810 Ok(Some(DispatchHandled::Generic(
811 DispatchDecision::FallThrough,
812 )))
813 }
814 _ => Ok(None),
815 }
816}