diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 6cbf35bacf6..b834eef1175 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -328,9 +328,9 @@ impl Context<'_> { self.vm.frame_mut().env_stack.pop(); } - for _ in 0..env_to_pop { - self.realm.environments.pop(); - } + let env_truncation_len = + self.realm.environments.len().saturating_sub(env_to_pop); + self.realm.environments.truncate(env_truncation_len); if target_address == catch_target { self.vm.frame_mut().pc = catch_target as usize; @@ -378,13 +378,16 @@ impl Context<'_> { self.vm.frame_mut().env_stack.pop(); } - for _ in 0..env_to_pop { - self.realm.environments.pop(); - } + let env_truncation_len = + self.realm.environments.len().saturating_sub(env_to_pop); + self.realm.environments.truncate(env_truncation_len); - for _ in 0..self.vm.frame().pop_on_return { - self.vm.pop(); - } + let previous_stack_size = self + .vm + .stack + .len() + .saturating_sub(self.vm.frame().pop_on_return); + self.vm.stack.truncate(previous_stack_size); self.vm.frame_mut().pop_on_return = 0; let record = AbruptCompletionRecord::default().with_throw_flag(); diff --git a/boa_engine/src/vm/opcode/control_flow/break.rs b/boa_engine/src/vm/opcode/control_flow/break.rs index 99bfe8b23b8..6866c13a12f 100644 --- a/boa_engine/src/vm/opcode/control_flow/break.rs +++ b/boa_engine/src/vm/opcode/control_flow/break.rs @@ -19,30 +19,23 @@ impl Operation for Break { // 1. Iterate through Env stack looking for exit address. let mut envs_to_pop = 0; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("EnvStackEntry must exist"); - - if (jump_address <= env_entry.exit_address()) + while let Some(env_entry) = context.vm.frame().env_stack.last() { + if (jump_address == env_entry.exit_address()) || (env_entry.is_finally_env() && jump_address == env_entry.start_address()) { break; } - if jump_address <= env_entry.exit_address() { + // Checks for the break if we have jumped from inside of a finally block + if jump_address == env_entry.exit_address() { break; } envs_to_pop += env_entry.env_num(); context.vm.frame_mut().env_stack.pop(); } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); // 2. Register target address in AbruptCompletionRecord. let new_record = AbruptCompletionRecord::default() @@ -77,14 +70,10 @@ impl Operation for Continue { // 1. Iterate through Env stack looking for exit address. let mut envs_to_pop = 0; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("EnvStackEntry must exist"); - + while let Some(env_entry) = context.vm.frame_mut().env_stack.last() { + // We check two conditions here where continue actually jumps to a higher address. + // 1. When we have reached a finally env that matches the jump address we are moving to. + // 2. When there is no finally, and we have reached the continue location. if (env_entry.is_finally_env() && jump_address == env_entry.start_address()) || (jump_address == target_address && jump_address == env_entry.start_address()) { @@ -92,6 +81,7 @@ impl Operation for Continue { } envs_to_pop += env_entry.env_num(); + // The below check determines whether we have continued from inside of a finally block. if jump_address > target_address && jump_address == env_entry.exit_address() { context.vm.frame_mut().env_stack.pop(); break; @@ -99,9 +89,8 @@ impl Operation for Continue { context.vm.frame_mut().env_stack.pop(); } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); // 2. Register target address in AbruptCompletionRecord. let new_record = AbruptCompletionRecord::default() diff --git a/boa_engine/src/vm/opcode/control_flow/catch.rs b/boa_engine/src/vm/opcode/control_flow/catch.rs index 38a9da3a7af..d7beb626360 100644 --- a/boa_engine/src/vm/opcode/control_flow/catch.rs +++ b/boa_engine/src/vm/opcode/control_flow/catch.rs @@ -44,23 +44,16 @@ impl Operation for CatchEnd { fn execute(context: &mut Context<'_>) -> JsResult { context.vm.frame_mut().try_catch.pop(); let mut envs_to_pop = 0_usize; - for _ in 1..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("this must exist"); - + while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() { envs_to_pop += env_entry.env_num(); + if env_entry.is_catch_env() { break; } } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); context.vm.frame_mut().finally_return = FinallyReturn::None; Ok(ShouldExit::False) @@ -79,25 +72,25 @@ impl Operation for CatchEnd2 { const INSTRUCTION: &'static str = "INST - CatchEnd2"; fn execute(context: &mut Context<'_>) -> JsResult { - let frame = context.vm.frame_mut(); - if frame + if let Some(catch_entry) = context + .vm + .frame() .env_stack .last() - .expect("Env stack entry must exist") - .is_catch_env() + .filter(|entry| entry.is_catch_env()) { - let catch_entry = frame - .env_stack - .pop() - .expect("must exist as catch env entry"); - - for _ in 0..catch_entry.env_num() { - context.realm.environments.pop(); - } + let env_truncation_len = context + .realm + .environments + .len() + .saturating_sub(catch_entry.env_num()); + context.realm.environments.truncate(env_truncation_len); + + context.vm.frame_mut().env_stack.pop(); } - if frame.finally_return == FinallyReturn::Err { - frame.finally_return = FinallyReturn::None; + if context.vm.frame_mut().finally_return == FinallyReturn::Err { + context.vm.frame_mut().finally_return = FinallyReturn::None; } Ok(ShouldExit::False) } diff --git a/boa_engine/src/vm/opcode/control_flow/finally.rs b/boa_engine/src/vm/opcode/control_flow/finally.rs index b11d2ad4702..dd7a392bd0e 100644 --- a/boa_engine/src/vm/opcode/control_flow/finally.rs +++ b/boa_engine/src/vm/opcode/control_flow/finally.rs @@ -60,17 +60,11 @@ impl Operation for FinallyEnd { if next_finally < record.target() { context.vm.frame_mut().pc = next_finally as usize; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("Environment stack entries must exist"); - + while let Some(env_entry) = context.vm.frame().env_stack.last() { if next_finally <= env_entry.exit_address() { break; } + envs_to_pop += env_entry.env_num(); context.vm.frame_mut().env_stack.pop(); } @@ -78,17 +72,11 @@ impl Operation for FinallyEnd { { // handle the continuation of an abrupt break. context.vm.frame_mut().pc = record.target() as usize; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("Environment stack entries must exist"); - + while let Some(env_entry) = context.vm.frame().env_stack.last() { if record.target() == env_entry.exit_address() { break; } + envs_to_pop += env_entry.env_num(); context.vm.frame_mut().env_stack.pop(); } @@ -99,14 +87,7 @@ impl Operation for FinallyEnd { { // Handle the continuation of an abrupt continue context.vm.frame_mut().pc = record.target() as usize; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("EnvStackEntry must exist"); - + while let Some(env_entry) = context.vm.frame().env_stack.last() { if env_entry.start_address() == record.target() { break; } @@ -117,9 +98,9 @@ impl Operation for FinallyEnd { context.vm.frame_mut().abrupt_completion = None; } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = + context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); } else { context.vm.frame_mut().env_stack.pop(); } @@ -133,17 +114,11 @@ impl Operation for FinallyEnd { if next_finally < record.target() { context.vm.frame_mut().pc = next_finally as usize; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .last() - .expect("Environment stack entries must exist"); - + while let Some(env_entry) = context.vm.frame().env_stack.last() { if next_finally <= env_entry.exit_address() { break; } + envs_to_pop += env_entry.env_num(); context.vm.frame_mut().env_stack.pop(); } @@ -151,14 +126,7 @@ impl Operation for FinallyEnd { && context.vm.frame().pc < record.target() as usize { context.vm.frame_mut().pc = record.target() as usize; - for _ in 0..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("EnvStackEntry must exist"); - + while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() { envs_to_pop += env_entry.env_num(); if env_entry.start_address() == record.target() { break; @@ -173,15 +141,20 @@ impl Operation for FinallyEnd { .pop() .expect("Popping current finally stack."); - for _ in 0..current_stack.env_num() { - context.realm.environments.pop(); - } + let env_truncation_len = context + .realm + .environments + .len() + .saturating_sub(current_stack.env_num()); + context.realm.environments.truncate(env_truncation_len); + return Err(JsError::from_opaque(context.vm.pop())); } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = + context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); + return Ok(ShouldExit::False); } let current_stack = context @@ -191,9 +164,13 @@ impl Operation for FinallyEnd { .pop() .expect("Popping current finally stack."); - for _ in 0..current_stack.env_num() { - context.realm.environments.pop(); - } + let env_truncation_len = context + .realm + .environments + .len() + .saturating_sub(current_stack.env_num()); + context.realm.environments.truncate(env_truncation_len); + Err(JsError::from_opaque(context.vm.pop())) } } diff --git a/boa_engine/src/vm/opcode/control_flow/labelled.rs b/boa_engine/src/vm/opcode/control_flow/labelled.rs index bf4a46512b3..45797e2448b 100644 --- a/boa_engine/src/vm/opcode/control_flow/labelled.rs +++ b/boa_engine/src/vm/opcode/control_flow/labelled.rs @@ -16,12 +16,12 @@ impl Operation for LabelledStart { fn execute(context: &mut Context<'_>) -> JsResult { let start = context.vm.frame().pc as u32 - 1; - let finally = context.vm.read::(); + let end = context.vm.read::(); context .vm .frame_mut() .env_stack - .push(EnvStackEntry::new(start, finally).with_labelled_flag()); + .push(EnvStackEntry::new(start, end).with_labelled_flag()); Ok(ShouldExit::False) } } @@ -29,7 +29,7 @@ impl Operation for LabelledStart { /// `LabelledEnd` implements the Opcode Operation for `Opcode::LabelledEnd` /// /// Operation: -/// - Clean up enviroments at the end of labelled block. +/// - Clean up environments at the end of labelled block. #[derive(Debug, Clone, Copy)] pub(crate) struct LabelledEnd; @@ -39,13 +39,7 @@ impl Operation for LabelledEnd { fn execute(context: &mut Context<'_>) -> JsResult { let mut envs_to_pop = 0_usize; - for _ in 1..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("this must exist"); + while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() { envs_to_pop += env_entry.env_num(); if env_entry.is_labelled_env() { @@ -53,9 +47,9 @@ impl Operation for LabelledEnd { } } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); + Ok(ShouldExit::False) } } diff --git a/boa_engine/src/vm/opcode/control_flow/try.rs b/boa_engine/src/vm/opcode/control_flow/try.rs index dc0cdb5337a..804209686e4 100644 --- a/boa_engine/src/vm/opcode/control_flow/try.rs +++ b/boa_engine/src/vm/opcode/control_flow/try.rs @@ -66,13 +66,7 @@ impl Operation for TryEnd { fn execute(context: &mut Context<'_>) -> JsResult { context.vm.frame_mut().try_catch.pop(); let mut envs_to_pop = 0_usize; - for _ in 1..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("this must exist"); + while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() { envs_to_pop += env_entry.env_num(); if env_entry.is_try_env() { @@ -80,9 +74,9 @@ impl Operation for TryEnd { } } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); + context.vm.frame_mut().finally_return = FinallyReturn::None; Ok(ShouldExit::False) } diff --git a/boa_engine/src/vm/opcode/iteration/loop_ops.rs b/boa_engine/src/vm/opcode/iteration/loop_ops.rs index b9226fc8880..c35d7ea3de2 100644 --- a/boa_engine/src/vm/opcode/iteration/loop_ops.rs +++ b/boa_engine/src/vm/opcode/iteration/loop_ops.rs @@ -43,25 +43,21 @@ impl Operation for LoopContinue { let exit = context.vm.read::(); // 1. Clean up the previous environment. - if context + if let Some(entry) = context .vm - .frame_mut() + .frame() .env_stack .last() - .expect("this must exist") - .exit_address() - == exit + .filter(|entry| entry.exit_address() == exit) { - let popped_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("EnvStackEntries must exist"); - - for _ in 0..popped_entry.env_num() { - context.realm.environments.pop(); - } + let env_truncation_len = context + .realm + .environments + .len() + .saturating_sub(entry.env_num()); + context.realm.environments.truncate(env_truncation_len); + + context.vm.frame_mut().env_stack.pop(); } // 2. Push a new clean EnvStack. @@ -88,13 +84,7 @@ impl Operation for LoopEnd { fn execute(context: &mut Context<'_>) -> JsResult { let mut envs_to_pop = 0_usize; - for _ in 1..context.vm.frame().env_stack.len() { - let env_entry = context - .vm - .frame_mut() - .env_stack - .pop() - .expect("this must exist"); + while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() { envs_to_pop += env_entry.env_num(); if env_entry.is_loop_env() { @@ -102,9 +92,9 @@ impl Operation for LoopEnd { } } - for _ in 0..envs_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(envs_to_pop); + context.realm.environments.truncate(env_truncation_len); + Ok(ShouldExit::False) } } diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index 5fb10d113f3..919d5103877 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -1148,11 +1148,15 @@ generate_impl! { /// Jumps to a target location and pops the environments involved. /// /// Operands: Jump Address: u32, Target address: u32 + /// + /// Stack: **=>** Break, /// Sets the `AbruptCompletionRecord` for a delayed continue /// /// Operands: Jump Address: u32, Target address: u32, + /// + /// Stack: **=>** Continue, /// Pops value converts it to boolean and pushes it back. @@ -1326,14 +1330,14 @@ generate_impl! { /// Push loop start marker. /// - /// Operands: Exit Address: u32 + /// Operands: Exit Address: `u32` /// /// Stack: **=>** LoopStart, /// Clean up environments when a loop continues. /// - /// Operands: + /// Operands: Start Address: `u32`, Exit Address: `u32` /// /// Stack: **=>** LoopContinue, diff --git a/boa_engine/src/vm/opcode/return_stm/mod.rs b/boa_engine/src/vm/opcode/return_stm/mod.rs index f35d55666f8..de96b9f2c9f 100644 --- a/boa_engine/src/vm/opcode/return_stm/mod.rs +++ b/boa_engine/src/vm/opcode/return_stm/mod.rs @@ -18,14 +18,7 @@ impl Operation for Return { let current_address = context.vm.frame().pc; let mut env_to_pop = 0; let mut finally_address = None; - while !context.vm.frame().env_stack.is_empty() { - let env_entry = context - .vm - .frame() - .env_stack - .last() - .expect("EnvStackEntry must exist"); - + while let Some(env_entry) = context.vm.frame().env_stack.last() { if env_entry.is_finally_env() { if (env_entry.start_address() as usize) < current_address { finally_address = Some(env_entry.exit_address() as usize); @@ -43,9 +36,8 @@ impl Operation for Return { context.vm.frame_mut().env_stack.pop(); } - for _ in 0..env_to_pop { - context.realm.environments.pop(); - } + let env_truncation_len = context.realm.environments.len().saturating_sub(env_to_pop); + context.realm.environments.truncate(env_truncation_len); if let Some(finally) = finally_address { context.vm.frame_mut().pc = finally;