diff --git a/crates/kernel/src/vm/activation.rs b/crates/kernel/src/vm/activation.rs index 75eba37e..f0ee04f9 100644 --- a/crates/kernel/src/vm/activation.rs +++ b/crates/kernel/src/vm/activation.rs @@ -359,6 +359,19 @@ impl Activation { Vec::from(&self.valstack[l - width..]) } + #[inline] + pub fn peek2(&self) -> (Var, Var) { + let l = self.valstack.len(); + let (a, b) = (&self.valstack[l - 1], &self.valstack[l - 2]); + (a.clone(), b.clone()) + } + + #[inline] + pub fn update(&mut self, amt: usize, v: Var) { + let l = self.valstack.len(); + self.valstack[l - amt - 1] = v; + } + #[inline] pub fn jump(&mut self, label_id: Label) { let label = &self.program.jump_labels[label_id.0 as usize]; diff --git a/crates/kernel/src/vm/exec_state.rs b/crates/kernel/src/vm/exec_state.rs index f59b3425..6b478e12 100644 --- a/crates/kernel/src/vm/exec_state.rs +++ b/crates/kernel/src/vm/exec_state.rs @@ -157,6 +157,18 @@ impl VMExecState { self.top().peek(amt) } + /// Return the top two values on the value stack. + #[inline] + pub(crate) fn peek2(&self) -> (Var, Var) { + self.top().peek2() + } + + /// Update at a set offset in the value stack. + #[inline] + pub(crate) fn update(&mut self, amt: usize, v: &Var) { + self.top_mut().update(amt, v.clone()) + } + /// Return the top of the value stack. #[inline] pub(crate) fn peek_top(&self) -> Var { diff --git a/crates/kernel/src/vm/vm_execute.rs b/crates/kernel/src/vm/vm_execute.rs index 5aa13757..35ceddcf 100644 --- a/crates/kernel/src/vm/vm_execute.rs +++ b/crates/kernel/src/vm/vm_execute.rs @@ -251,10 +251,7 @@ impl VM { id, } => { // Pull the range ends off the stack. - // TODO LambdaMOO had optimization here where it would only peek and update. - // But I had some difficulty getting stack values right, so will do this simpler - // for now and revisit later. - let (to, from) = (&state.pop(), &state.pop()); + let (to, from) = state.peek2(); // TODO: LambdaMOO has special handling for MAXINT/MAXOBJ // Given we're 64-bit this is highly unlikely to ever be a concern for us, but @@ -263,30 +260,38 @@ impl VM { let next_val = match (to.variant(), from.variant()) { (Variant::Int(to_i), Variant::Int(from_i)) => { if from_i > to_i { + state.pop(); + state.pop(); state.jump(label); + continue; } v_int(from_i + 1) } (Variant::Obj(to_o), Variant::Obj(from_o)) => { if from_o.0 > to_o.0 { + state.pop(); + state.pop(); state.jump(label); + continue; } v_obj(from_o.0 + 1) } (_, _) => { + state.pop(); + state.pop(); // Make sure we've jumped out of the loop before raising the error, // because in verbs that aren't `d' we could end up continuing on in // the loop (with a messed up stack) otherwise. state.jump(label); + return self.raise_error(state, E_TYPE); } }; - state.set_env(id, from); - state.push(&next_val); - state.push(to); + state.update(1, &next_val); + state.set_env(id, &from); } Op::Pop => { state.pop();