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