From dbbcc57809b8911ab5ee8bcf3ed2cf5bcf697358 Mon Sep 17 00:00:00 2001 From: creampnx_x Date: Sun, 18 Sep 2022 21:48:42 +0000 Subject: [PATCH] Fix labelled block statement (#2285) This Pull Request fixes [x-after-break-to-label](https://github.com/tc39/test262/blob/dc1dc28aa46d9457e47550b34e6f25a8b80de826/test/language/block-scope/leave/x-after-break-to-label.js) ### Example ```js { let x = 2; L: { let x = 3; console.log(x === 3); break L; console.log(false); } console.log(x === 2); } ``` ### Previously > Uncaught "SyntaxError": "Cannot use the undeclared label 'L'" ### Now > true
true ### What did I do 1. add `lable` to `Node::Block` 2. push labelled-block's `control info` to `jump_info` list 3. pop it before `Opcode::PopEnvironment` Co-authored-by: creampnx_x <2270436024@qq.com> --- boa_engine/src/bytecompiler/mod.rs | 41 +++++++++++++++++++ boa_engine/src/syntax/ast/node/block/mod.rs | 11 ++++- .../parser/statement/labelled_stm/mod.rs | 1 + boa_engine/src/tests.rs | 19 +++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index b7be4ac51b2..3cad0ab5fbb 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -57,6 +57,7 @@ enum JumpControlInfoKind { Loop, Switch, Try, + LabelledBlock, } #[derive(Debug, Clone, Copy)] @@ -491,6 +492,36 @@ impl<'b> ByteCompiler<'b> { } } + #[inline] + fn push_labelled_block_control_info(&mut self, label: Sym, start_address: u32) { + self.jump_info.push(JumpControlInfo { + label: Some(label), + start_address, + kind: JumpControlInfoKind::LabelledBlock, + breaks: Vec::new(), + try_continues: Vec::new(), + in_catch: false, + has_finally: false, + finally_start: None, + for_of_in_loop: false, + }); + } + + #[inline] + fn pop_labelled_block_control_info(&mut self) { + let info = self.jump_info.pop().expect("no jump information found"); + + assert!(info.kind == JumpControlInfoKind::LabelledBlock); + + for label in info.breaks { + self.patch_jump(label); + } + + for label in info.try_continues { + self.patch_jump_with_target(label, info.start_address); + } + } + #[inline] fn compile_access(node: &Node) -> Option> { match node { @@ -1797,6 +1828,11 @@ impl<'b> ByteCompiler<'b> { } } Node::Block(block) => { + if let Some(label) = block.label() { + let next = self.next_opcode_location(); + self.push_labelled_block_control_info(label, next); + } + self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); @@ -1807,6 +1843,11 @@ impl<'b> ByteCompiler<'b> { let index_compile_environment = self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, index_compile_environment as u32); + + if block.label().is_some() { + self.pop_labelled_block_control_info(); + } + self.emit_opcode(Opcode::PopEnvironment); } Node::Throw(throw) => { diff --git a/boa_engine/src/syntax/ast/node/block/mod.rs b/boa_engine/src/syntax/ast/node/block/mod.rs index 16f5f22df11..f6495006f38 100644 --- a/boa_engine/src/syntax/ast/node/block/mod.rs +++ b/boa_engine/src/syntax/ast/node/block/mod.rs @@ -25,11 +25,11 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-BlockStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "deser", serde(transparent))] #[derive(Clone, Debug, PartialEq)] pub struct Block { #[cfg_attr(feature = "deser", serde(flatten))] statements: StatementList, + label: Option, } impl Block { @@ -52,6 +52,14 @@ impl Block { " ".repeat(indentation) ) } + + pub fn label(&self) -> Option { + self.label + } + + pub fn set_label(&mut self, label: Sym) { + self.label = Some(label); + } } impl From for Block @@ -61,6 +69,7 @@ where fn from(list: T) -> Self { Self { statements: list.into(), + label: None, } } } diff --git a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs index 82b56533927..60b3c79171f 100644 --- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs @@ -95,6 +95,7 @@ fn set_label_for_node(node: &mut Node, name: Sym) { Node::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name), Node::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name), Node::WhileLoop(ref mut while_loop) => while_loop.set_label(name), + Node::Block(ref mut block) => block.set_label(name), _ => (), } } diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs index 24cbb02005d..6aceff0cf60 100644 --- a/boa_engine/src/tests.rs +++ b/boa_engine/src/tests.rs @@ -1632,3 +1632,22 @@ fn test_empty_statement() { "#; assert_eq!(&exec(src), "10"); } + +#[test] +fn test_labelled_block() { + let src = r#" + let result = true; + { + let x = 2; + L: { + let x = 3; + result &&= (x === 3); + break L; + result &&= (false); + } + result &&= (x === 2); + } + result; + "#; + assert_eq!(&exec(src), "true"); +}