From 540b20fe8b972af855b58351a466800e592c58b6 Mon Sep 17 00:00:00 2001 From: Ryan Daum Date: Sat, 21 Dec 2024 17:00:15 -0500 Subject: [PATCH] Rework VM dispatching Reworks so that the MOO execution loop doesn't get access to the whole exec state, activation, etc. and simply gets the stack frame and returns the next operation to complete. Avoids some borrowing shenanigans that were getting vexing. --- crates/kernel/src/builtins/bf_objects.rs | 14 +- crates/kernel/src/tasks/vm_host.rs | 91 +++++++++--- crates/kernel/src/vm/mod.rs | 26 +++- crates/kernel/src/vm/moo_execute.rs | 180 +++++++++-------------- crates/kernel/src/vm/moo_frame.rs | 9 +- crates/kernel/src/vm/vm_call.rs | 4 +- 6 files changed, 175 insertions(+), 149 deletions(-) diff --git a/crates/kernel/src/builtins/bf_objects.rs b/crates/kernel/src/builtins/bf_objects.rs index 4e1b3773..9b665147 100644 --- a/crates/kernel/src/builtins/bf_objects.rs +++ b/crates/kernel/src/builtins/bf_objects.rs @@ -30,7 +30,7 @@ use crate::bf_declare; use crate::builtins::BfRet::{Ret, VmInstr}; use crate::builtins::{world_state_bf_err, BfCallState, BfErr, BfRet, BuiltinFunction}; use crate::tasks::VerbCall; -use crate::vm::ExecutionResult::ContinueVerb; +use crate::vm::ExecutionResult::DispatchVerb; lazy_static! { static ref INITIALIZE_SYM: Symbol = Symbol::mk("initialize"); @@ -158,7 +158,7 @@ fn bf_create(bf_args: &mut BfCallState<'_>) -> Result { let bf_frame = bf_args.bf_frame_mut(); bf_frame.bf_trampoline = Some(BF_CREATE_OBJECT_TRAMPOLINE_DONE); bf_frame.bf_trampoline_arg = Some(v_obj(new_obj.clone())); - Ok(VmInstr(ContinueVerb { + Ok(VmInstr(DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, @@ -269,7 +269,7 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { bf_frame.bf_trampoline = Some(BF_RECYCLE_TRAMPOLINE_CALL_EXITFUNC); bf_frame.bf_trampoline_arg = Some(contents); - return Ok(VmInstr(ContinueVerb { + return Ok(VmInstr(DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, @@ -338,7 +338,7 @@ fn bf_recycle(bf_args: &mut BfCallState<'_>) -> Result { bf_frame.bf_trampoline = Some(BF_RECYCLE_TRAMPOLINE_CALL_EXITFUNC); // Call :exitfunc on the head object. - return Ok(VmInstr(ContinueVerb { + return Ok(VmInstr(DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, @@ -440,7 +440,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { let bf_frame = bf_args.bf_frame_mut(); bf_frame.bf_trampoline = Some(BF_MOVE_TRAMPOLINE_MOVE_CALL_EXITFUNC); bf_frame.bf_trampoline_arg = None; - return Ok(VmInstr(ContinueVerb { + return Ok(VmInstr(DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, @@ -518,7 +518,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { bf_frame.bf_trampoline = Some(BF_MOVE_TRAMPOLINE_CALL_ENTERFUNC); bf_frame.bf_trampoline_arg = None; - let continuation = ContinueVerb { + let continuation = DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, @@ -565,7 +565,7 @@ fn bf_move(bf_args: &mut BfCallState<'_>) -> Result { bf_frame.bf_trampoline = Some(BF_MOVE_TRAMPOLINE_DONE); bf_frame.bf_trampoline_arg = None; - return Ok(VmInstr(ContinueVerb { + return Ok(VmInstr(DispatchVerb { permissions: bf_args.task_perms_who(), resolved_verb, binary, diff --git a/crates/kernel/src/tasks/vm_host.rs b/crates/kernel/src/tasks/vm_host.rs index 2152c3f2..3524936a 100644 --- a/crates/kernel/src/tasks/vm_host.rs +++ b/crates/kernel/src/tasks/vm_host.rs @@ -248,22 +248,47 @@ impl VmHost { // Grant the loop its next tick slice. self.vm_exec_state.tick_slice = self.max_ticks - self.vm_exec_state.tick_count; - let pre_exec_tick_count = self.vm_exec_state.tick_count; - // Actually invoke the VM, asking it to loop until it's ready to yield back to us. let mut result = self.run_interpreter(&exec_params, world_state, session.clone()); - - let post_exec_tick_count = self.vm_exec_state.tick_count; - trace!( - task_id, - executed_ticks = post_exec_tick_count - pre_exec_tick_count, - ?result, - "Executed ticks", - ); while self.is_running() { match result { ExecutionResult::More => return ContinueOk, - ExecutionResult::ContinueVerb { + ExecutionResult::PushError(e) => { + result = self.vm_exec_state.push_error(e); + continue; + } + ExecutionResult::RaiseError(e) => { + result = self.vm_exec_state.raise_error(e); + continue; + } + ExecutionResult::Return(value) => { + result = self + .vm_exec_state + .unwind_stack(FinallyReason::Return(value)); + continue; + } + ExecutionResult::Unwind(fr) => { + result = self.vm_exec_state.unwind_stack(fr); + continue; + } + ExecutionResult::Pass(pass_args) => { + result = self + .vm_exec_state + .prepare_pass_verb(world_state, &pass_args); + continue; + } + ExecutionResult::PrepareVerbDispatch { + this, + verb_name, + args, + } => { + result = self + .vm_exec_state + .verb_dispatch(&exec_params, world_state, this, verb_name, args) + .unwrap_or_else(|e| ExecutionResult::PushError(e)); + continue; + } + ExecutionResult::DispatchVerb { permissions, resolved_verb, binary, @@ -310,7 +335,19 @@ impl VmHost { ); continue; } - ExecutionResult::DispatchFork(fork_request) => { + ExecutionResult::DispatchFork(delay, task_id, fv_offset) => { + let a = self.vm_exec_state.top().clone(); + let parent_task_id = self.vm_exec_state.task_id; + let new_activation = a.clone(); + let fork_request = Fork { + player: a.player.clone(), + progr: a.permissions.clone(), + parent_task_id, + delay, + activation: new_activation, + fork_vector_offset: fv_offset, + task_id, + }; return DispatchFork(fork_request); } ExecutionResult::Suspend(delay) => { @@ -371,23 +408,31 @@ impl VmHost { } // Pick the right kind of execution flow depending on the activation -- builtin or MOO? - // (To avoid borrow issues on the exec state, we won't actually pull the frame out, just look - // at its type and execute the right function which will have to unpack the frame itself. - // this is a bit not-ideal but it's the best I can do right now.) - let result = match &self.vm_exec_state.top().frame { - Frame::Moo(_) => { - return moo_frame_execute( - vm_exec_params, - &mut self.vm_exec_state, + let mut tick_count = self.vm_exec_state.tick_count; + let tick_slice = self.vm_exec_state.tick_slice; + let activation = self.vm_exec_state.top_mut(); + + let (result, new_tick_count) = match &mut activation.frame { + Frame::Moo(fr) => { + let result = moo_frame_execute( + tick_slice, + &mut tick_count, + activation.permissions.clone(), + fr, world_state, - session.clone(), ); + (result, tick_count) } Frame::Bf(_) => { - self.vm_exec_state - .reenter_builtin_function(vm_exec_params, world_state, session) + let result = self.vm_exec_state.reenter_builtin_function( + vm_exec_params, + world_state, + session, + ); + (result, tick_count) } }; + self.vm_exec_state.tick_count = new_tick_count; result } diff --git a/crates/kernel/src/vm/mod.rs b/crates/kernel/src/vm/mod.rs index 4cfb2500..953885d0 100644 --- a/crates/kernel/src/vm/mod.rs +++ b/crates/kernel/src/vm/mod.rs @@ -27,7 +27,7 @@ use moor_compiler::{BuiltinId, Name}; use moor_compiler::{Offset, Program}; use moor_values::matching::command_parse::ParsedCommand; use moor_values::model::VerbDef; -use moor_values::{Obj, Var}; +use moor_values::{Error, List, Obj, Symbol, Var}; pub use vm_call::VerbExecutionRequest; pub use vm_unwind::FinallyReason; @@ -83,12 +83,30 @@ pub struct VmExecParams { pub enum ExecutionResult { /// Execution of this call stack is complete. Complete(Var), + /// An error occurred during execution, that we might need to push to the stack and + /// potentially resume or unwind, depending on the context. + PushError(Error), + /// An error occurred during execution, that should definitely be treated as a proper "raise" + /// and unwind event unless there's a catch handler in place + RaiseError(Error), + /// An explicit stack unwind (for a reason other than a return. + Unwind(FinallyReason), + /// Explicit return, unwind stack + Return(Var), + /// Create the frames necessary to perform a `pass` up the inheritance chain. + Pass(List), /// All is well. The task should let the VM continue executing. More, /// An exception was raised during execution. Exception(FinallyReason), - /// Request dispatch to another verb - ContinueVerb { + /// Begin preparing to call a verb, by looking up the verb and preparing the dispatch. + PrepareVerbDispatch { + this: Var, + verb_name: Symbol, + args: List, + }, + /// Perform the verb dispatch, building the stack frame and executing it. + DispatchVerb { /// The applicable permissions context. permissions: Obj, /// The requested verb. @@ -101,7 +119,7 @@ pub enum ExecutionResult { command: Option, }, /// Request dispatch of a new task as a fork - DispatchFork(Fork), + DispatchFork(Option, Option, Offset), /// Request dispatch of a builtin function with the given arguments. ContinueBuiltin { builtin: BuiltinId, diff --git a/crates/kernel/src/vm/moo_execute.rs b/crates/kernel/src/vm/moo_execute.rs index 64b41201..9d5167c7 100644 --- a/crates/kernel/src/vm/moo_execute.rs +++ b/crates/kernel/src/vm/moo_execute.rs @@ -12,16 +12,13 @@ // this program. If not, see . // -use crate::tasks::sessions::Session; -use crate::vm::activation::Frame; -use crate::vm::moo_frame::{CatchType, ScopeType}; +use crate::vm::moo_frame::{CatchType, MooStackFrame, ScopeType}; use crate::vm::vm_unwind::FinallyReason; -use crate::vm::{ExecutionResult, Fork, VMExecState, VmExecParams}; +use crate::vm::ExecutionResult; use lazy_static::lazy_static; -use moor_compiler::{to_literal, Op, ScatterLabel}; +use moor_compiler::{Op, ScatterLabel}; use moor_values::model::WorldState; use std::ops::Add; -use std::sync::Arc; use std::time::Duration; use moor_values::Error::{E_ARGS, E_DIV, E_INVARG, E_INVIND, E_TYPE, E_VARNF}; @@ -54,7 +51,7 @@ macro_rules! binary_var_op { Ok(result) => $f.poke(0, result), Err(err_code) => { $f.pop(); - return $state.push_error(err_code); + return ExecutionResult::PushError(err_code); } } }; @@ -62,22 +59,14 @@ macro_rules! binary_var_op { /// Main VM opcode execution for MOO stack frames. The actual meat of the MOO virtual machine. pub fn moo_frame_execute( - exec_params: &VmExecParams, - state: &mut VMExecState, + tick_slice: usize, + tick_count: &mut usize, + permissions: Obj, + f: &mut MooStackFrame, world_state: &mut dyn WorldState, - session: Arc, ) -> ExecutionResult { - let opcodes = { - // Check the frame type to verify it's MOO, before doing anything else - let a = state.top_mut(); - let Frame::Moo(ref mut f) = a.frame else { - panic!("Unsupported VM stack frame type"); - }; - - // We clone the main vector here to avoid borrowing issues with the frame later, as we - // need to modify the program counter. - f.program.main_vector.clone() - }; + // To avoid borrowing issues when mutating the frame elsewhere... + let opcodes = f.program.main_vector.clone(); // Special case for empty opcodes set, just return v_none() immediately. if opcodes.is_empty() { @@ -95,15 +84,9 @@ pub fn moo_frame_execute( // and the variable `tick_slice_count` that slice's progress. // `max_ticks` on the task is the total limit which is checked above us, outside this loop. let mut tick_slice_count = 0; - while tick_slice_count < state.tick_slice { + while tick_slice_count < tick_slice { tick_slice_count += 1; - state.tick_count += 1; - - // Borrow the top of the activation stack for the lifetime of this execution. - let a = state.top_mut(); - let Frame::Moo(ref mut f) = a.frame else { - panic!("Unsupported VM stack frame type"); - }; + *tick_count += 1; // Otherwise, start poppin' opcodes. // We panic here if we run out of opcodes, as that means there's a bug in either the @@ -169,7 +152,7 @@ pub fn moo_frame_execute( // didn't 'throw' and unwind the stack -- we need to get out of the loop. // So we preemptively jump (here and below for List) and then raise the error. f.jump(end_label); - return state.raise_error(E_TYPE); + return ExecutionResult::RaiseError(E_TYPE); }; let count = *count as usize; let Variant::List(l) = list.variant() else { @@ -177,7 +160,7 @@ pub fn moo_frame_execute( f.pop(); f.jump(end_label); - return state.raise_error(E_TYPE); + return ExecutionResult::RaiseError(E_TYPE); }; // If we've exhausted the list, pop the count and list and jump out. @@ -239,7 +222,7 @@ pub fn moo_frame_execute( // the loop (with a messed up stack) otherwise. f.jump(end_label); - return state.raise_error(E_TYPE); + return ExecutionResult::RaiseError(E_TYPE); } }; (from.clone(), next_val) @@ -292,7 +275,7 @@ pub fn moo_frame_execute( let (tail, list) = (f.pop(), f.peek_top_mut()); if !list.is_sequence() || list.type_code() == VarType::TYPE_STR { f.pop(); - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } // TODO: quota check SVO_MAX_LIST_CONCAT -> E_QUOTA in list add and append let result = list.push(&tail); @@ -302,7 +285,7 @@ pub fn moo_frame_execute( } Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -312,12 +295,12 @@ pub fn moo_frame_execute( // Don't allow strings here. if list.type_code() == VarType::TYPE_STR { f.pop(); - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } if !tail.is_sequence() || !list.is_sequence() { f.pop(); - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } let new_list = list.append(&tail); match new_list { @@ -326,7 +309,7 @@ pub fn moo_frame_execute( } Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -339,7 +322,7 @@ pub fn moo_frame_execute( } Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -359,7 +342,7 @@ pub fn moo_frame_execute( } Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -368,20 +351,20 @@ pub fn moo_frame_execute( let contents = f.pop(); // Contents must be a list let Variant::List(contents) = contents.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; let mut slots = Vec::with_capacity(*num_slots); for _ in 0..*num_slots { let (k, v) = (f.pop(), f.pop()); let Variant::Str(k) = k.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; let sym = Symbol::mk_case_insensitive(k.as_string()); slots.push((sym, v)); } let delegate = f.pop(); let Variant::Obj(delegate) = delegate.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; // Slots should be v_str -> value, num_slots times @@ -423,7 +406,7 @@ pub fn moo_frame_execute( } Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -439,7 +422,7 @@ pub fn moo_frame_execute( // `inf`. let divargs = f.peek_range(2); if matches!(divargs[1].variant(), Variant::Int(0) | Variant::Float(0.0)) { - return state.push_error(E_DIV); + return ExecutionResult::PushError(E_DIV); }; binary_var_op!(self, f, state, div); } @@ -452,7 +435,7 @@ pub fn moo_frame_execute( Op::Mod => { let divargs = f.peek_range(2); if matches!(divargs[1].variant(), Variant::Int(0) | Variant::Float(0.0)) { - return state.push_error(E_DIV); + return ExecutionResult::PushError(E_DIV); }; binary_var_op!(self, f, state, modulus); } @@ -481,14 +464,14 @@ pub fn moo_frame_execute( match v.negative() { Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } Ok(v) => f.poke(0, v), } } Op::Push(ident) => { let Some(v) = f.get_env(ident) else { - return state.push_error(E_VARNF); + return ExecutionResult::PushError(E_VARNF); }; f.push(v.clone()); } @@ -503,7 +486,7 @@ pub fn moo_frame_execute( Ok(v) => f.push(v), Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -515,7 +498,7 @@ pub fn moo_frame_execute( Ok(v) => f.poke(0, v), Err(e) => { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -524,7 +507,7 @@ pub fn moo_frame_execute( let result = base.range(&from, &to, IndexMode::OneBased); if let Err(e) = result { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } f.poke(0, result.unwrap()); } @@ -533,7 +516,7 @@ pub fn moo_frame_execute( let result = base.range_set(&from, &to, &value, IndexMode::OneBased); if let Err(e) = result { f.pop(); - return state.push_error(e); + return ExecutionResult::PushError(e); } f.poke(0, result.unwrap()); } @@ -541,23 +524,23 @@ pub fn moo_frame_execute( let v = f.peek_abs(offset.0 as usize); match v.len() { Ok(l) => f.push(v_int(l as i64)), - Err(e) => return state.push_error(e), + Err(e) => return ExecutionResult::PushError(e), } } Op::GetProp => { let (propname, obj) = (f.pop(), f.peek_top()); let Variant::Str(propname) = propname.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; - let value = get_property(world_state, &a.permissions, obj, propname); + let value = get_property(world_state, &permissions, obj, propname); match value { Ok(v) => { f.poke(0, v); } Err(e) => { - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -565,16 +548,16 @@ pub fn moo_frame_execute( let (propname, obj) = f.peek2(); let Variant::Str(propname) = propname.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; - let value = get_property(world_state, &a.permissions, obj, propname); + let value = get_property(world_state, &permissions, obj, propname); match value { Ok(v) => { f.push(v); } Err(e) => { - return state.push_error(e); + return ExecutionResult::PushError(e); } } } @@ -584,20 +567,20 @@ pub fn moo_frame_execute( let (propname, obj) = match (propname.variant(), obj.variant()) { (Variant::Str(propname), Variant::Obj(obj)) => (propname, obj), (_, _) => { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } }; let propname = Symbol::mk_case_insensitive(propname.as_string()); let update_result = - world_state.update_property(&a.permissions, obj, propname, &rhs.clone()); + world_state.update_property(&permissions, obj, propname, &rhs.clone()); match update_result { Ok(()) => { f.poke(0, rhs); } Err(e) => { - return state.push_error(e.to_error_code()); + return ExecutionResult::PushError(e.to_error_code()); } } } @@ -609,70 +592,56 @@ pub fn moo_frame_execute( Variant::Int(time) => *time as f64, Variant::Float(time) => *time, _ => { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } }; if time < 0.0 { - return state.push_error(E_INVARG); + return ExecutionResult::PushError(E_INVARG); } let delay = (time != 0.0).then(|| Duration::from_secs_f64(time)); - let new_activation = a.clone(); - let fork = Fork { - player: a.player.clone(), - progr: a.permissions.clone(), - parent_task_id: state.task_id, - delay, - activation: new_activation, - fork_vector_offset: *fv_offset, - task_id: *id, - }; - return ExecutionResult::DispatchFork(fork); + + return ExecutionResult::DispatchFork(delay, *id, *fv_offset); } Op::Pass => { let args = f.pop(); let Variant::List(args) = args.variant() else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; - return state.prepare_pass_verb(world_state, args); + return ExecutionResult::Pass(args.clone()); } Op::CallVerb => { let (args, verb, obj) = (f.pop(), f.pop(), f.pop()); let (Variant::List(l), Variant::Str(s)) = (args.variant(), verb.variant()) else { - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; let verb = Symbol::mk_case_insensitive(s.as_string()); - let result = state.verb_dispatch(exec_params, world_state, obj, verb, l.clone()); - match result { - Ok(r) => return r, - Err(e) => { - return state.push_error(e); - } - } + return ExecutionResult::PrepareVerbDispatch { + this: obj, + verb_name: verb, + args: l.clone(), + }; } Op::Return => { let ret_val = f.pop(); - return state.unwind_stack(FinallyReason::Return(ret_val)); + return ExecutionResult::Return(ret_val); } Op::Return0 => { - return state.unwind_stack(FinallyReason::Return(v_int(0))); + return ExecutionResult::Return(v_int(0)); } Op::Done => { - return state.unwind_stack(FinallyReason::Return(v_none())); + return ExecutionResult::Return(v_none()); } Op::FuncCall { id } => { // Pop arguments, should be a list. let args = f.pop(); let Variant::List(args) = args.variant() else { - return state.push_error(E_ARGS); + return ExecutionResult::PushError(E_ARGS); + }; + return ExecutionResult::ContinueBuiltin { + builtin: *id, + arguments: args.iter().collect(), }; - return state.call_builtin_function( - *id, - args.iter().collect(), - exec_params, - world_state, - session, - ); } Op::PushCatchLabel(label) => { // Get the error codes, which is either a list of error codes or Any. @@ -728,12 +697,7 @@ pub fn moo_frame_execute( let handler = f.pop_scope().expect("Missing handler for try/catch/except"); let ScopeType::TryCatch(..) = handler.scope_type else { - panic!( - "Handler is not a catch handler; {}:{} line {}", - to_literal(&a.this), - a.verb_name, - f.find_line_no(f.pc - 1).unwrap() - ); + panic!("Handler is not a catch handler",); }; if is_catch { @@ -757,7 +721,7 @@ pub fn moo_frame_execute( FinallyReason::Raise(_) | FinallyReason::Return(_) | FinallyReason::Exit { .. } => { - return state.unwind_stack(why); + return ExecutionResult::Unwind(why); } } } @@ -775,7 +739,7 @@ pub fn moo_frame_execute( continue; } Op::Exit { stack, label } => { - return state.unwind_stack(FinallyReason::Exit { + return ExecutionResult::Unwind(FinallyReason::Exit { stack: *stack, label: *label, }); @@ -803,7 +767,7 @@ pub fn moo_frame_execute( let rhs = f.peek_top(); let Variant::List(rhs_values) = rhs.variant() else { f.pop(); - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); }; rhs_values.clone() }; @@ -811,7 +775,7 @@ pub fn moo_frame_execute( let len = rhs_values.len(); if len < nreq || !have_rest && len > nargs { f.pop(); - return state.push_error(E_ARGS); + return ExecutionResult::PushError(E_ARGS); } let mut nopt_avail = len - nreq; let nrest = if have_rest && len >= nargs { @@ -837,7 +801,7 @@ pub fn moo_frame_execute( } ScatterLabel::Required(id) => { let Some(arg) = args_iter.next() else { - return state.push_error(E_ARGS); + return ExecutionResult::PushError(E_ARGS); }; f.set_env(id, arg.clone()); @@ -846,7 +810,7 @@ pub fn moo_frame_execute( if nopt_avail > 0 { nopt_avail -= 1; let Some(arg) = args_iter.next() else { - return state.push_error(E_ARGS); + return ExecutionResult::PushError(E_ARGS); }; f.set_env(id, arg.clone()); } else if jump_where.is_none() && jump_to.is_some() { @@ -863,7 +827,7 @@ pub fn moo_frame_execute( Op::CheckListForSplice => { if !f.peek_top().is_sequence() { f.pop(); - return state.push_error(E_TYPE); + return ExecutionResult::PushError(E_TYPE); } } } diff --git a/crates/kernel/src/vm/moo_frame.rs b/crates/kernel/src/vm/moo_frame.rs index b980e66f..deface41 100644 --- a/crates/kernel/src/vm/moo_frame.rs +++ b/crates/kernel/src/vm/moo_frame.rs @@ -176,12 +176,12 @@ impl MooStackFrame { program, environment, environment_width, - valstack: vec![], - scope_stack: vec![], pc: 0, temp: v_none(), - catch_stack: vec![], - finally_stack: vec![], + valstack: Default::default(), + scope_stack: Default::default(), + catch_stack: Default::default(), + finally_stack: Default::default(), } } @@ -306,7 +306,6 @@ impl MooStackFrame { // If this is a lexical scope, expand the environment to accommodate the new variables. // (This is just updating environment_width) let environment_width = environment_width as usize; - assert!(environment_width <= self.environment.len()); self.environment_width += environment_width; let end_pos = self.program.jump_labels[end_label.0 as usize].position.0 as usize; diff --git a/crates/kernel/src/vm/vm_call.rs b/crates/kernel/src/vm/vm_call.rs index 6ba1c3fd..17118e5a 100644 --- a/crates/kernel/src/vm/vm_call.rs +++ b/crates/kernel/src/vm/vm_call.rs @@ -178,7 +178,7 @@ impl VMExecState { // Permissions for the activation are the verb's owner. let permissions = resolved_verb.owner(); - ExecutionResult::ContinueVerb { + ExecutionResult::DispatchVerb { permissions, resolved_verb, binary, @@ -231,7 +231,7 @@ impl VMExecState { caller, }; - ExecutionResult::ContinueVerb { + ExecutionResult::DispatchVerb { permissions: permissions.clone(), resolved_verb, binary,