Browse Source

Fix labelled block statement (#2285)

<!---
Thank you for contributing to Boa! Please fill out the template below, and remove or add any
information as you feel neccesary.
--->

This Pull Request fixes [x-after-break-to-label](dc1dc28aa4/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 <br> 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>
pull/2288/head
creampnx_x 2 years ago
parent
commit
dbbcc57809
  1. 41
      boa_engine/src/bytecompiler/mod.rs
  2. 11
      boa_engine/src/syntax/ast/node/block/mod.rs
  3. 1
      boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
  4. 19
      boa_engine/src/tests.rs

41
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<Access<'_>> {
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) => {

11
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<Sym>,
}
impl Block {
@ -52,6 +52,14 @@ impl Block {
" ".repeat(indentation)
)
}
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
}
impl<T> From<T> for Block
@ -61,6 +69,7 @@ where
fn from(list: T) -> Self {
Self {
statements: list.into(),
label: None,
}
}
}

1
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),
_ => (),
}
}

19
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");
}

Loading…
Cancel
Save