diff --git a/boa_engine/src/bytecompiler/statement/break.rs b/boa_engine/src/bytecompiler/statement/break.rs index f175565f8ca..342a9fa14a7 100644 --- a/boa_engine/src/bytecompiler/statement/break.rs +++ b/boa_engine/src/bytecompiler/statement/break.rs @@ -8,6 +8,12 @@ use boa_interner::Sym; impl ByteCompiler<'_, '_> { /// Compile a [`Break`] `boa_ast` node pub(crate) fn compile_break(&mut self, node: Break) { + let opcode = if node.label().is_some() { + Opcode::BreakLabel + } else { + Opcode::Break + }; + if let Some(info) = self.jump_info.last().filter(|info| info.is_try_block()) { let in_finally = info.in_finally(); let in_catch_no_finally = info.in_catch() && !info.has_finally(); @@ -20,8 +26,7 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::CatchEnd2); } - let (break_label, target_jump_label) = - self.emit_opcode_with_two_operands(Opcode::Break); + let (break_label, target_jump_label) = self.emit_opcode_with_two_operands(opcode); if let Some(node_label) = node.label() { self.search_jump_info_label(target_jump_label, node_label); @@ -48,15 +53,12 @@ impl ByteCompiler<'_, '_> { } // Emit the break opcode -> (Label, Label) - let (break_label, target_label) = self.emit_opcode_with_two_operands(Opcode::Break); - if node.label().is_some() { - self.search_jump_info_label( - break_label, - node.label().expect("must exist in this block"), - ); - self.search_jump_info_label(target_label, node.label().expect("must exist")); + let (break_label, target_label) = self.emit_opcode_with_two_operands(opcode); + if let Some(label) = node.label() { + self.search_jump_info_label(break_label, label); + self.search_jump_info_label(target_label, label); return; - }; + } let info = self .jump_info diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index ef27b8f4d46..9255ab213e3 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -329,6 +329,7 @@ impl CodeBlock { } Opcode::CopyDataProperties | Opcode::Break + | Opcode::BreakLabel | Opcode::Continue | Opcode::LoopStart | Opcode::IteratorLoopStart @@ -616,8 +617,7 @@ impl CodeBlock { | Opcode::Reserved49 | Opcode::Reserved50 | Opcode::Reserved51 - | Opcode::Reserved52 - | Opcode::Reserved53 => unreachable!("Reserved opcodes are unrechable"), + | Opcode::Reserved52 => unreachable!("Reserved opcodes are unrechable"), } } } diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index 870437af72c..3bd5e589c99 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -154,7 +154,7 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Opcode::Break => { + Opcode::Break | Opcode::BreakLabel => { let jump_operand = self.read::(pc); pc += size_of::(); let target_operand = self.read::(pc); @@ -709,8 +709,7 @@ impl CodeBlock { | Opcode::Reserved49 | Opcode::Reserved50 | Opcode::Reserved51 - | Opcode::Reserved52 - | Opcode::Reserved53 => unreachable!("Reserved opcodes are unrechable"), + | Opcode::Reserved52 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/boa_engine/src/vm/opcode/control_flow/break.rs b/boa_engine/src/vm/opcode/control_flow/break.rs index 86c8058cd8b..ce1476383fb 100644 --- a/boa_engine/src/vm/opcode/control_flow/break.rs +++ b/boa_engine/src/vm/opcode/control_flow/break.rs @@ -37,7 +37,7 @@ impl Operation for Break { continue; } - if (jump_address == env_entry.exit_address()) + if jump_address == env_entry.exit_address() || (env_entry.is_finally_env() && jump_address == env_entry.start_address()) { found_target = true; @@ -45,12 +45,53 @@ impl Operation for Break { continue; } - // Checks for the break if we have jumped from inside of a finally block - if jump_address == env_entry.exit_address() { - found_target = true; - set_loop_result = env_entry.set_loop_return_value(value.clone()); - continue; + envs_to_pop += env_entry.env_num(); + context.vm.frame_mut().env_stack.pop(); + } + + let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop); + context.vm.environments.truncate(env_truncation_len); + + // 2. Register target address in AbruptCompletionRecord. + let new_record = AbruptCompletionRecord::new_break().with_initial_target(target_address); + context.vm.frame_mut().abrupt_completion = Some(new_record); + + // 3. Set program counter and finally return fields. + context.vm.frame_mut().pc = jump_address; + Ok(CompletionType::Normal) + } +} + +/// `BreakLabel` implements the Opcode Operation for `Opcode::BreakLabel` +/// +/// Operation: +/// - Pop required environments and jump to address. +pub(crate) struct BreakLabel; + +impl Operation for BreakLabel { + const NAME: &'static str = "BreakLabel"; + const INSTRUCTION: &'static str = "INST - BreakLabel"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let jump_address = context.vm.read::(); + let target_address = context.vm.read::(); + + let value = context.vm.stack.pop().unwrap_or(JsValue::undefined()); + context.vm.push(value); + + // 1. Iterate through Env stack looking for exit address. + let mut envs_to_pop = 0; + for i in (0..context.vm.frame().env_stack.len()).rev() { + let Some(env_entry) = context.vm.frame_mut().env_stack.get_mut(i) else { + break; + }; + + if jump_address == env_entry.exit_address() + || (env_entry.is_finally_env() && jump_address == env_entry.start_address()) + { + break; } + envs_to_pop += env_entry.env_num(); context.vm.frame_mut().env_stack.pop(); } diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index 038d899d4d8..46d50109092 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -1180,6 +1180,13 @@ generate_impl! { /// Stack: loop_return_value **=>** Break, + /// Jumps to a label target location and pops the environments involved. + /// + /// Operands: Jump Address: u32, Target address: u32 + /// + /// Stack: **=>** + BreakLabel, + /// Sets the `AbruptCompletionRecord` for a delayed continue /// /// Operands: Jump Address: u32, Target address: u32, @@ -1809,8 +1816,6 @@ generate_impl! { Reserved51 => Reserved, /// Reserved [`Opcode`]. Reserved52 => Reserved, - /// Reserved [`Opcode`]. - Reserved53 => Reserved, } }