Browse Source

Implement `LabelledStatement` (#2349)

This Pull Request changes the following:

- Implements the `LabelledStatement` Parse node.
- Removes `label` from all label-able items (switch, blocks and loop statements).
- Adjusts parsing to the new AST.

#2295 isn't fixed by this, but with this change it should be easier to fix.
pull/2369/head
José Julián Espina 2 years ago
parent
commit
d4c220a70b
  1. 670
      boa_engine/src/bytecompiler/mod.rs
  2. 3
      boa_engine/src/syntax/ast/declaration/mod.rs
  3. 14
      boa_engine/src/syntax/ast/statement/block.rs
  4. 28
      boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs
  5. 22
      boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs
  6. 20
      boa_engine/src/syntax/ast/statement/iteration/for_loop.rs
  7. 24
      boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs
  8. 28
      boa_engine/src/syntax/ast/statement/iteration/while_loop.rs
  9. 127
      boa_engine/src/syntax/ast/statement/labelled.rs
  10. 45
      boa_engine/src/syntax/ast/statement/mod.rs
  11. 18
      boa_engine/src/syntax/ast/statement/return.rs
  12. 14
      boa_engine/src/syntax/ast/statement_list/mod.rs
  13. 9
      boa_engine/src/syntax/parser/error.rs
  14. 1
      boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs
  15. 9
      boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs
  16. 4
      boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs
  17. 6
      boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs
  18. 155
      boa_engine/src/syntax/parser/function/tests.rs
  19. 4
      boa_engine/src/syntax/parser/statement/block/tests.rs
  20. 55
      boa_engine/src/syntax/parser/statement/if_stm/mod.rs
  21. 11
      boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs
  22. 29
      boa_engine/src/syntax/parser/statement/iteration/for_statement.rs
  23. 13
      boa_engine/src/syntax/parser/statement/iteration/while_statement.rs
  24. 40
      boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
  25. 4
      boa_engine/src/syntax/parser/statement/return_stm/mod.rs
  26. 3
      boa_engine/src/syntax/parser/tests.rs

670
boa_engine/src/bytecompiler/mod.rs

@ -20,7 +20,10 @@ use crate::{
}, },
pattern::{Pattern, PatternArrayElement, PatternObjectElement}, pattern::{Pattern, PatternArrayElement, PatternObjectElement},
property::{MethodDefinition, PropertyDefinition, PropertyName}, property::{MethodDefinition, PropertyDefinition, PropertyName},
statement::iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, statement::{
iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer},
Block, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, LabelledItem, WhileLoop,
},
Declaration, Expression, Statement, StatementList, StatementListItem, Declaration, Expression, Statement, StatementList, StatementListItem,
}, },
vm::{BindingOpcode, CodeBlock, Opcode}, vm::{BindingOpcode, CodeBlock, Opcode},
@ -1553,335 +1556,417 @@ impl<'b> ByteCompiler<'b> {
} }
#[inline] #[inline]
pub fn compile_stmt(&mut self, node: &Statement, use_expr: bool) -> JsResult<()> { pub fn compile_for_loop(&mut self, for_loop: &ForLoop, label: Option<Sym>) -> JsResult<()> {
match node { self.context.push_compile_time_environment(false);
Statement::Var(var) => self.compile_var_decl(var)?, let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
Statement::If(node) => {
self.compile_expr(node.cond(), true)?;
let jelse = self.jump_if_false();
self.compile_stmt(node.body(), false)?;
match node.else_node() { if let Some(init) = for_loop.init() {
None => { match init {
self.patch_jump(jelse); ForLoopInitializer::Expression(expr) => self.compile_expr(expr, false)?,
} ForLoopInitializer::Var(decl) => {
Some(else_body) => { self.create_decls_from_var_decl(decl);
let exit = self.jump(); self.compile_var_decl(decl)?;
self.patch_jump(jelse); }
self.compile_stmt(else_body, false)?; ForLoopInitializer::Lexical(decl) => {
self.patch_jump(exit); self.create_decls_from_lexical_decl(decl);
} self.compile_lexical_decl(decl)?;
} }
} }
Statement::ForLoop(for_loop) => { }
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
if let Some(init) = for_loop.init() { self.emit_opcode(Opcode::LoopStart);
match init { let initial_jump = self.jump();
ForLoopInitializer::Expression(expr) => self.compile_expr(expr, false)?,
ForLoopInitializer::Var(decl) => {
self.create_decls_from_var_decl(decl);
self.compile_var_decl(decl)?;
}
ForLoopInitializer::Lexical(decl) => {
self.create_decls_from_lexical_decl(decl);
self.compile_lexical_decl(decl)?;
}
}
}
self.emit_opcode(Opcode::LoopStart); let start_address = self.next_opcode_location();
let initial_jump = self.jump(); self.push_loop_control_info(label, start_address);
let start_address = self.next_opcode_location(); self.emit_opcode(Opcode::LoopContinue);
self.push_loop_control_info(for_loop.label(), start_address); if let Some(final_expr) = for_loop.final_expr() {
self.compile_expr(final_expr, false)?;
}
self.emit_opcode(Opcode::LoopContinue); self.patch_jump(initial_jump);
if let Some(final_expr) = for_loop.final_expr() {
self.compile_expr(final_expr, false)?;
}
self.patch_jump(initial_jump); if let Some(condition) = for_loop.condition() {
self.compile_expr(condition, true)?;
} else {
self.emit_opcode(Opcode::PushTrue);
}
let exit = self.jump_if_false();
if let Some(condition) = for_loop.condition() { self.compile_stmt(for_loop.body(), false)?;
self.compile_expr(condition, true)?;
} else {
self.emit_opcode(Opcode::PushTrue);
}
let exit = self.jump_if_false();
self.compile_stmt(for_loop.body(), false)?; self.emit(Opcode::Jump, &[start_address]);
self.emit(Opcode::Jump, &[start_address]); self.patch_jump(exit);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
self.patch_jump(exit); let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
self.pop_loop_control_info(); let index_compile_environment = self.push_compile_environment(compile_environment);
self.emit_opcode(Opcode::LoopEnd); 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);
self.emit_opcode(Opcode::PopEnvironment);
Ok(())
}
let (num_bindings, compile_environment) = #[inline]
self.context.pop_compile_time_environment(); pub fn compile_for_in_loop(
let index_compile_environment = self.push_compile_environment(compile_environment); &mut self,
self.patch_jump_with_target(push_env.0, num_bindings as u32); for_in_loop: &ForInLoop,
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); label: Option<Sym>,
self.emit_opcode(Opcode::PopEnvironment); ) -> JsResult<()> {
} let init_bound_names = for_in_loop.init().bound_names();
Statement::ForInLoop(for_in_loop) => { if init_bound_names.is_empty() {
let init_bound_names = for_in_loop.init().bound_names(); self.compile_expr(for_in_loop.expr(), true)?;
if init_bound_names.is_empty() { } else {
self.compile_expr(for_in_loop.expr(), true)?; self.context.push_compile_time_environment(false);
} else { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names { for name in init_bound_names {
self.context.create_mutable_binding(name, false); self.context.create_mutable_binding(name, false);
} }
self.compile_expr(for_in_loop.expr(), true)?; self.compile_expr(for_in_loop.expr(), true)?;
let (num_bindings, compile_environment) = let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
self.context.pop_compile_time_environment(); let index_compile_environment = self.push_compile_environment(compile_environment);
let index_compile_environment = self.patch_jump_with_target(push_env.0, num_bindings as u32);
self.push_compile_environment(compile_environment); self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.patch_jump_with_target(push_env.0, num_bindings as u32); self.emit_opcode(Opcode::PopEnvironment);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); }
self.emit_opcode(Opcode::PopEnvironment);
}
let early_exit = self.emit_opcode_with_operand(Opcode::ForInLoopInitIterator); let early_exit = self.emit_opcode_with_operand(Opcode::ForInLoopInitIterator);
self.emit_opcode(Opcode::LoopStart); self.emit_opcode(Opcode::LoopStart);
let start_address = self.next_opcode_location(); let start_address = self.next_opcode_location();
self.push_loop_control_info_for_of_in_loop(for_in_loop.label(), start_address); self.push_loop_control_info_for_of_in_loop(label, start_address);
self.emit_opcode(Opcode::LoopContinue); self.emit_opcode(Opcode::LoopContinue);
self.context.push_compile_time_environment(false); self.context.push_compile_time_environment(false);
let push_env = let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); let exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext);
let exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext);
match for_in_loop.init() { match for_in_loop.init() {
IterableLoopInitializer::Identifier(ident) => { IterableLoopInitializer::Identifier(ident) => {
self.context.create_mutable_binding(*ident, true); self.context.create_mutable_binding(*ident, true);
let binding = self.context.set_mutable_binding(*ident); let binding = self.context.set_mutable_binding(*ident);
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitVar, &[index]); self.emit(Opcode::DefInitVar, &[index]);
}
IterableLoopInitializer::Var(declaration) => match declaration {
Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitVar, *ident);
}
Binding::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
} }
IterableLoopInitializer::Var(declaration) => match declaration { self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
Binding::Identifier(ident) => { }
self.context.create_mutable_binding(*ident, true); },
self.emit_binding(BindingOpcode::InitVar, *ident); IterableLoopInitializer::Let(declaration) => match declaration {
} Binding::Identifier(ident) => {
Binding::Pattern(pattern) => { self.context.create_mutable_binding(*ident, false);
for ident in pattern.idents() { self.emit_binding(BindingOpcode::InitLet, *ident);
self.context.create_mutable_binding(ident, true); }
} Binding::Pattern(pattern) => {
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; for ident in pattern.idents() {
} self.context.create_mutable_binding(ident, false);
}, }
IterableLoopInitializer::Let(declaration) => match declaration { self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?;
Binding::Identifier(ident) => { }
self.context.create_mutable_binding(*ident, false); },
self.emit_binding(BindingOpcode::InitLet, *ident); IterableLoopInitializer::Const(declaration) => match declaration {
} Binding::Identifier(ident) => {
Binding::Pattern(pattern) => { self.context.create_immutable_binding(*ident);
for ident in pattern.idents() { self.emit_binding(BindingOpcode::InitConst, *ident);
self.context.create_mutable_binding(ident, false); }
} Binding::Pattern(pattern) => {
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; for ident in pattern.idents() {
} self.context.create_immutable_binding(ident);
},
IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => {
self.context.create_immutable_binding(*ident);
self.emit_binding(BindingOpcode::InitConst, *ident);
}
Binding::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_immutable_binding(ident);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?;
}
},
IterableLoopInitializer::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
} }
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?;
} }
},
IterableLoopInitializer::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
}
}
self.compile_stmt(for_in_loop.body(), false)?; self.compile_stmt(for_in_loop.body(), false)?;
let (num_bindings, compile_environment) = let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
self.context.pop_compile_time_environment(); let index_compile_environment = self.push_compile_environment(compile_environment);
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.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); self.emit_opcode(Opcode::PopEnvironment);
self.emit_opcode(Opcode::PopEnvironment);
self.emit(Opcode::Jump, &[start_address]); self.emit(Opcode::Jump, &[start_address]);
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::IteratorClose); self.emit_opcode(Opcode::IteratorClose);
self.patch_jump(early_exit); self.patch_jump(early_exit);
} Ok(())
Statement::ForOfLoop(for_of_loop) => { }
let init_bound_names = for_of_loop.init().bound_names();
if init_bound_names.is_empty() {
self.compile_expr(for_of_loop.iterable(), true)?;
} else {
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names { #[inline]
self.context.create_mutable_binding(name, false); pub fn compile_for_of_loop(
} &mut self,
self.compile_expr(for_of_loop.iterable(), true)?; for_of_loop: &ForOfLoop,
label: Option<Sym>,
) -> JsResult<()> {
let init_bound_names = for_of_loop.init().bound_names();
if init_bound_names.is_empty() {
self.compile_expr(for_of_loop.iterable(), true)?;
} else {
self.context.push_compile_time_environment(false);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let (num_bindings, compile_environment) = for name in init_bound_names {
self.context.pop_compile_time_environment(); self.context.create_mutable_binding(name, false);
let index_compile_environment = }
self.push_compile_environment(compile_environment); self.compile_expr(for_of_loop.iterable(), true)?;
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);
self.emit_opcode(Opcode::PopEnvironment);
}
if for_of_loop.r#await() { let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
self.emit_opcode(Opcode::InitIteratorAsync); let index_compile_environment = self.push_compile_environment(compile_environment);
} else { self.patch_jump_with_target(push_env.0, num_bindings as u32);
self.emit_opcode(Opcode::InitIterator); self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
} self.emit_opcode(Opcode::PopEnvironment);
}
self.emit_opcode(Opcode::LoopStart); if for_of_loop.r#await() {
let start_address = self.next_opcode_location(); self.emit_opcode(Opcode::InitIteratorAsync);
self.push_loop_control_info_for_of_in_loop(for_of_loop.label(), start_address); } else {
self.emit_opcode(Opcode::LoopContinue); self.emit_opcode(Opcode::InitIterator);
}
self.context.push_compile_time_environment(false); self.emit_opcode(Opcode::LoopStart);
let push_env = let start_address = self.next_opcode_location();
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); self.push_loop_control_info_for_of_in_loop(label, start_address);
self.emit_opcode(Opcode::LoopContinue);
let exit = if for_of_loop.r#await() { self.context.push_compile_time_environment(false);
self.emit_opcode(Opcode::ForAwaitOfLoopIterate); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
self.emit_opcode(Opcode::Await);
self.emit_opcode(Opcode::GeneratorNext); let exit = if for_of_loop.r#await() {
self.emit_opcode_with_operand(Opcode::ForAwaitOfLoopNext) self.emit_opcode(Opcode::ForAwaitOfLoopIterate);
} else { self.emit_opcode(Opcode::Await);
self.emit_opcode_with_operand(Opcode::ForInLoopNext) self.emit_opcode(Opcode::GeneratorNext);
}; self.emit_opcode_with_operand(Opcode::ForAwaitOfLoopNext)
} else {
self.emit_opcode_with_operand(Opcode::ForInLoopNext)
};
match for_of_loop.init() { match for_of_loop.init() {
IterableLoopInitializer::Identifier(ref ident) => { IterableLoopInitializer::Identifier(ref ident) => {
self.context.create_mutable_binding(*ident, true); self.context.create_mutable_binding(*ident, true);
let binding = self.context.set_mutable_binding(*ident); let binding = self.context.set_mutable_binding(*ident);
let index = self.get_or_insert_binding(binding); let index = self.get_or_insert_binding(binding);
self.emit(Opcode::DefInitVar, &[index]); self.emit(Opcode::DefInitVar, &[index]);
}
IterableLoopInitializer::Var(declaration) => match declaration {
Binding::Identifier(ident) => {
self.context.create_mutable_binding(*ident, true);
self.emit_binding(BindingOpcode::InitVar, *ident);
}
Binding::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
} }
IterableLoopInitializer::Var(declaration) => match declaration { self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
Binding::Identifier(ident) => { }
self.context.create_mutable_binding(*ident, true); },
self.emit_binding(BindingOpcode::InitVar, *ident); IterableLoopInitializer::Let(declaration) => match declaration {
} Binding::Identifier(ident) => {
Binding::Pattern(pattern) => { self.context.create_mutable_binding(*ident, false);
for ident in pattern.idents() { self.emit_binding(BindingOpcode::InitLet, *ident);
self.context.create_mutable_binding(ident, true); }
} Binding::Pattern(pattern) => {
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; for ident in pattern.idents() {
} self.context.create_mutable_binding(ident, false);
}, }
IterableLoopInitializer::Let(declaration) => match declaration { self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?;
Binding::Identifier(ident) => { }
self.context.create_mutable_binding(*ident, false); },
self.emit_binding(BindingOpcode::InitLet, *ident); IterableLoopInitializer::Const(declaration) => match declaration {
} Binding::Identifier(ident) => {
Binding::Pattern(pattern) => { self.context.create_immutable_binding(*ident);
for ident in pattern.idents() { self.emit_binding(BindingOpcode::InitConst, *ident);
self.context.create_mutable_binding(ident, false); }
} Binding::Pattern(pattern) => {
self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; for ident in pattern.idents() {
} self.context.create_immutable_binding(ident);
},
IterableLoopInitializer::Const(declaration) => match declaration {
Binding::Identifier(ident) => {
self.context.create_immutable_binding(*ident);
self.emit_binding(BindingOpcode::InitConst, *ident);
}
Binding::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_immutable_binding(ident);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?;
}
},
IterableLoopInitializer::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
} }
self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?;
} }
},
IterableLoopInitializer::Pattern(pattern) => {
for ident in pattern.idents() {
self.context.create_mutable_binding(ident, true);
}
self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?;
}
}
self.compile_stmt(for_of_loop.body(), false)?; self.compile_stmt(for_of_loop.body(), false)?;
let (num_bindings, compile_environment) = let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
self.context.pop_compile_time_environment(); let index_compile_environment = self.push_compile_environment(compile_environment);
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.0, num_bindings as u32); self.patch_jump_with_target(push_env.1, index_compile_environment as u32);
self.patch_jump_with_target(push_env.1, index_compile_environment as u32); self.emit_opcode(Opcode::PopEnvironment);
self.emit_opcode(Opcode::PopEnvironment);
self.emit(Opcode::Jump, &[start_address]); self.emit(Opcode::Jump, &[start_address]);
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::IteratorClose); self.emit_opcode(Opcode::IteratorClose);
} Ok(())
Statement::WhileLoop(while_) => { }
self.emit_opcode(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info(while_.label(), start_address);
self.emit_opcode(Opcode::LoopContinue);
self.compile_expr(while_.condition(), true)?; #[inline]
let exit = self.jump_if_false(); pub fn compile_while_loop(
self.compile_stmt(while_.body(), false)?; &mut self,
self.emit(Opcode::Jump, &[start_address]); while_loop: &WhileLoop,
self.patch_jump(exit); label: Option<Sym>,
) -> JsResult<()> {
self.emit_opcode(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info(label, start_address);
self.emit_opcode(Opcode::LoopContinue);
self.compile_expr(while_loop.condition(), true)?;
let exit = self.jump_if_false();
self.compile_stmt(while_loop.body(), false)?;
self.emit(Opcode::Jump, &[start_address]);
self.patch_jump(exit);
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
Ok(())
}
self.pop_loop_control_info(); #[inline]
self.emit_opcode(Opcode::LoopEnd); pub fn compile_do_while_loop(
} &mut self,
Statement::DoWhileLoop(do_while) => { do_while_loop: &DoWhileLoop,
self.emit_opcode(Opcode::LoopStart); label: Option<Sym>,
let initial_label = self.jump(); ) -> JsResult<()> {
self.emit_opcode(Opcode::LoopStart);
let initial_label = self.jump();
let start_address = self.next_opcode_location(); let start_address = self.next_opcode_location();
self.push_loop_control_info(do_while.label(), start_address); self.push_loop_control_info(label, start_address);
self.emit_opcode(Opcode::LoopContinue); self.emit_opcode(Opcode::LoopContinue);
let condition_label_address = self.next_opcode_location(); let condition_label_address = self.next_opcode_location();
self.compile_expr(do_while.cond(), true)?; self.compile_expr(do_while_loop.cond(), true)?;
let exit = self.jump_if_false(); let exit = self.jump_if_false();
self.patch_jump(initial_label); self.patch_jump(initial_label);
self.compile_stmt(do_while.body(), false)?; self.compile_stmt(do_while_loop.body(), false)?;
self.emit(Opcode::Jump, &[condition_label_address]); self.emit(Opcode::Jump, &[condition_label_address]);
self.patch_jump(exit); self.patch_jump(exit);
self.pop_loop_control_info(); self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::LoopEnd);
Ok(())
}
#[inline]
pub fn compile_block(
&mut self,
block: &Block,
label: Option<Sym>,
use_expr: bool,
) -> JsResult<()> {
if let Some(label) = 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);
self.create_decls(block.statement_list());
self.compile_statement_list(block.statement_list(), use_expr)?;
let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
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 label.is_some() {
self.pop_labelled_block_control_info();
}
self.emit_opcode(Opcode::PopEnvironment);
Ok(())
}
#[inline]
pub fn compile_stmt(&mut self, node: &Statement, use_expr: bool) -> JsResult<()> {
match node {
Statement::Var(var) => self.compile_var_decl(var)?,
Statement::If(node) => {
self.compile_expr(node.cond(), true)?;
let jelse = self.jump_if_false();
self.compile_stmt(node.body(), false)?;
match node.else_node() {
None => {
self.patch_jump(jelse);
}
Some(else_body) => {
let exit = self.jump();
self.patch_jump(jelse);
self.compile_stmt(else_body, false)?;
self.patch_jump(exit);
}
}
} }
Statement::ForLoop(for_loop) => self.compile_for_loop(for_loop, None)?,
Statement::ForInLoop(for_in_loop) => self.compile_for_in_loop(for_in_loop, None)?,
Statement::ForOfLoop(for_of_loop) => self.compile_for_of_loop(for_of_loop, None)?,
Statement::WhileLoop(while_loop) => self.compile_while_loop(while_loop, None)?,
Statement::DoWhileLoop(do_while_loop) => {
self.compile_do_while_loop(do_while_loop, None)?;
}
Statement::Block(block) => self.compile_block(block, None, use_expr)?,
Statement::Labelled(labelled) => match labelled.item() {
LabelledItem::Statement(stmt) => match stmt {
Statement::ForLoop(for_loop) => {
self.compile_for_loop(for_loop, Some(labelled.label()))?;
}
Statement::ForInLoop(for_in_loop) => {
self.compile_for_in_loop(for_in_loop, Some(labelled.label()))?;
}
Statement::ForOfLoop(for_of_loop) => {
self.compile_for_of_loop(for_of_loop, Some(labelled.label()))?;
}
Statement::WhileLoop(while_loop) => {
self.compile_while_loop(while_loop, Some(labelled.label()))?;
}
Statement::DoWhileLoop(do_while_loop) => {
self.compile_do_while_loop(do_while_loop, Some(labelled.label()))?;
}
Statement::Block(block) => {
self.compile_block(block, Some(labelled.label()), use_expr)?;
}
stmt => self.compile_stmt(stmt, use_expr)?,
},
LabelledItem::Function(f) => {
self.function(f.into(), NodeKind::Declaration, false)?;
}
},
Statement::Continue(node) => { Statement::Continue(node) => {
let next = self.next_opcode_location(); let next = self.next_opcode_location();
if let Some(info) = self if let Some(info) = self
@ -2020,29 +2105,6 @@ impl<'b> ByteCompiler<'b> {
.push(label); .push(label);
} }
} }
Statement::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);
self.create_decls(block.statement_list());
self.compile_statement_list(block.statement_list(), use_expr)?;
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
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);
}
Statement::Throw(throw) => { Statement::Throw(throw) => {
self.compile_expr(throw.expr(), true)?; self.compile_expr(throw.expr(), true)?;
self.emit(Opcode::Throw, &[]); self.emit(Opcode::Throw, &[]);

3
boa_engine/src/syntax/ast/declaration/mod.rs

@ -1,4 +1,5 @@
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use tap::Tap;
use super::{ use super::{
expression::Identifier, expression::Identifier,
@ -40,7 +41,7 @@ impl Declaration {
Declaration::AsyncFunction(af) => af.to_indented_string(interner, indentation), Declaration::AsyncFunction(af) => af.to_indented_string(interner, indentation),
Declaration::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation), Declaration::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation),
Declaration::Class(c) => c.to_indented_string(interner, indentation), Declaration::Class(c) => c.to_indented_string(interner, indentation),
Declaration::Lexical(l) => l.to_interned_string(interner), Declaration::Lexical(l) => l.to_interned_string(interner).tap_mut(|s| s.push(';')),
} }
} }

14
boa_engine/src/syntax/ast/statement/block.rs

@ -3,7 +3,7 @@
use crate::syntax::ast::{expression::Identifier, ContainsSymbol, StatementList}; use crate::syntax::ast::{expression::Identifier, ContainsSymbol, StatementList};
use super::Statement; use super::Statement;
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
/// A `block` statement (or compound statement in other languages) is used to group zero or /// A `block` statement (or compound statement in other languages) is used to group zero or
/// more statements. /// more statements.
@ -25,7 +25,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct Block { pub struct Block {
#[cfg_attr(feature = "deser", serde(flatten))] #[cfg_attr(feature = "deser", serde(flatten))]
statements: StatementList, statements: StatementList,
label: Option<Sym>,
} }
impl Block { impl Block {
@ -49,20 +48,12 @@ impl Block {
) )
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
#[inline] #[inline]
pub(crate) fn contains_arguments(&self) -> bool { pub(crate) fn contains_arguments(&self) -> bool {
self.statements.contains_arguments() self.statements.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.statements.contains(symbol) self.statements.contains(symbol)
} }
@ -75,7 +66,6 @@ where
fn from(list: T) -> Self { fn from(list: T) -> Self {
Self { Self {
statements: list.into(), statements: list.into(),
label: None,
} }
} }
} }

28
boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs

@ -1,5 +1,5 @@
use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
/// The `do...while` statement creates a loop that executes a specified statement until the /// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false. /// test condition evaluates to false.
@ -18,7 +18,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct DoWhileLoop { pub struct DoWhileLoop {
body: Box<Statement>, body: Box<Statement>,
condition: Expression, condition: Expression,
label: Option<Sym>,
} }
impl DoWhileLoop { impl DoWhileLoop {
@ -29,45 +28,26 @@ impl DoWhileLoop {
pub fn cond(&self) -> &Expression { pub fn cond(&self) -> &Expression {
&self.condition &self.condition
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
/// Creates a `DoWhileLoop` AST node. /// Creates a `DoWhileLoop` AST node.
pub fn new(body: Statement, condition: Expression) -> Self { pub fn new(body: Statement, condition: Expression) -> Self {
Self { Self {
body: body.into(), body: body.into(),
condition, condition,
label: None,
} }
} }
/// Converts the "do while" loop to a string with the given indentation. /// Converts the "do while" loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label { format!(
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
"do {} while ({})", "do {} while ({})",
self.body().to_indented_string(interner, indentation), self.body().to_indented_string(interner, indentation),
self.cond().to_interned_string(interner) self.cond().to_interned_string(interner)
)); )
buf
} }
#[inline] #[inline]
pub(crate) fn contains_arguments(&self) -> bool { pub(crate) fn contains_arguments(&self) -> bool {
self.body.contains_arguments() self.body.contains_arguments() || self.condition.contains_arguments()
|| self.condition.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

22
boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs

@ -3,14 +3,13 @@ use crate::syntax::ast::{
statement::{iteration::IterableLoopInitializer, Statement}, statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol, ContainsSymbol,
}; };
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct ForInLoop { pub struct ForInLoop {
init: IterableLoopInitializer, init: IterableLoopInitializer,
expr: Expression, expr: Expression,
body: Box<Statement>, body: Box<Statement>,
label: Option<Sym>,
} }
impl ForInLoop { impl ForInLoop {
@ -19,7 +18,6 @@ impl ForInLoop {
init, init,
expr, expr,
body: body.into(), body: body.into(),
label: None,
} }
} }
@ -35,26 +33,13 @@ impl ForInLoop {
&self.body &self.body
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
/// Converts the "for in" loop to a string with the given indentation. /// Converts the "for in" loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label { let mut buf = format!(
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
"for ({} in {}) ", "for ({} in {}) ",
self.init.to_interned_string(interner), self.init.to_interned_string(interner),
self.expr.to_interned_string(interner) self.expr.to_interned_string(interner)
)); );
buf.push_str(&self.body().to_indented_string(interner, indentation)); buf.push_str(&self.body().to_indented_string(interner, indentation));
buf buf
@ -65,7 +50,6 @@ impl ForInLoop {
self.init.contains_arguments() self.init.contains_arguments()
|| self.expr.contains_arguments() || self.expr.contains_arguments()
|| self.body.contains_arguments() || self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

20
boa_engine/src/syntax/ast/statement/iteration/for_loop.rs

@ -4,7 +4,7 @@ use crate::syntax::ast::{
statement::Statement, statement::Statement,
ContainsSymbol, Expression, ContainsSymbol, Expression,
}; };
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
/// The `for` statement creates a loop that consists of three optional expressions. /// The `for` statement creates a loop that consists of three optional expressions.
/// ///
@ -22,7 +22,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct ForLoop { pub struct ForLoop {
#[cfg_attr(feature = "deser", serde(flatten))] #[cfg_attr(feature = "deser", serde(flatten))]
inner: Box<InnerForLoop>, inner: Box<InnerForLoop>,
label: Option<Sym>,
} }
impl ForLoop { impl ForLoop {
@ -35,7 +34,6 @@ impl ForLoop {
) -> Self { ) -> Self {
Self { Self {
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)), inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
label: None,
} }
} }
@ -61,12 +59,7 @@ impl ForLoop {
/// Converts the for loop to a string with the given indentation. /// Converts the for loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label { let mut buf = String::from("for (");
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str("for (");
if let Some(init) = self.init() { if let Some(init) = self.init() {
buf.push_str(&init.to_interned_string(interner)); buf.push_str(&init.to_interned_string(interner));
} }
@ -86,14 +79,6 @@ impl ForLoop {
buf buf
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
#[inline] #[inline]
pub(crate) fn contains_arguments(&self) -> bool { pub(crate) fn contains_arguments(&self) -> bool {
let inner = &self.inner; let inner = &self.inner;
@ -101,7 +86,6 @@ impl ForLoop {
|| matches!(inner.condition, Some(ref expr) if expr.contains_arguments()) || matches!(inner.condition, Some(ref expr) if expr.contains_arguments())
|| matches!(inner.final_expr, Some(ref expr) if expr.contains_arguments()) || matches!(inner.final_expr, Some(ref expr) if expr.contains_arguments())
|| inner.body.contains_arguments() || inner.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

24
boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs

@ -3,7 +3,7 @@ use crate::syntax::ast::{
statement::{iteration::IterableLoopInitializer, Statement}, statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol, ContainsSymbol,
}; };
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -14,7 +14,6 @@ pub struct ForOfLoop {
init: IterableLoopInitializer, init: IterableLoopInitializer,
iterable: Expression, iterable: Expression,
body: Box<Statement>, body: Box<Statement>,
label: Option<Sym>,
r#await: bool, r#await: bool,
} }
@ -30,7 +29,6 @@ impl ForOfLoop {
init, init,
iterable, iterable,
body: body.into(), body: body.into(),
label: None,
r#await, r#await,
} }
} }
@ -47,14 +45,6 @@ impl ForOfLoop {
&self.body &self.body
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
/// Returns true if this "for...of" loop is an "for await...of" loop. /// Returns true if this "for...of" loop is an "for await...of" loop.
pub(crate) fn r#await(&self) -> bool { pub(crate) fn r#await(&self) -> bool {
self.r#await self.r#await
@ -62,19 +52,12 @@ impl ForOfLoop {
/// Converts the "for of" loop to a string with the given indentation. /// Converts the "for of" loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label { format!(
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
"for ({} of {}) {}", "for ({} of {}) {}",
self.init.to_interned_string(interner), self.init.to_interned_string(interner),
self.iterable.to_interned_string(interner), self.iterable.to_interned_string(interner),
self.body().to_indented_string(interner, indentation) self.body().to_indented_string(interner, indentation)
)); )
buf
} }
#[inline] #[inline]
@ -82,7 +65,6 @@ impl ForOfLoop {
self.init.contains_arguments() self.init.contains_arguments()
|| self.iterable.contains_arguments() || self.iterable.contains_arguments()
|| self.body.contains_arguments() || self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

28
boa_engine/src/syntax/ast/statement/iteration/while_loop.rs

@ -1,5 +1,5 @@
use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, Sym, ToInternedString}; use boa_interner::{Interner, ToInternedString};
/// The `while` statement creates a loop that executes a specified statement as long as the /// The `while` statement creates a loop that executes a specified statement as long as the
/// test condition evaluates to `true`. /// test condition evaluates to `true`.
/// ///
@ -16,7 +16,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct WhileLoop { pub struct WhileLoop {
condition: Expression, condition: Expression,
body: Box<Statement>, body: Box<Statement>,
label: Option<Sym>,
} }
impl WhileLoop { impl WhileLoop {
@ -27,44 +26,25 @@ impl WhileLoop {
pub fn body(&self) -> &Statement { pub fn body(&self) -> &Statement {
&self.body &self.body
} }
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
/// Creates a `WhileLoop` AST node. /// Creates a `WhileLoop` AST node.
pub fn new(condition: Expression, body: Statement) -> Self { pub fn new(condition: Expression, body: Statement) -> Self {
Self { Self {
condition, condition,
body: body.into(), body: body.into(),
label: None,
} }
} }
/// Converts the while loop to a string with the given indentation. /// Converts the while loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label { format!(
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
"while ({}) {}", "while ({}) {}",
self.condition().to_interned_string(interner), self.condition().to_interned_string(interner),
self.body().to_indented_string(interner, indentation) self.body().to_indented_string(interner, indentation)
)); )
buf
} }
pub(crate) fn contains_arguments(&self) -> bool { pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments() self.condition.contains_arguments() || self.body.contains_arguments()
|| self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

127
boa_engine/src/syntax/ast/statement/labelled.rs

@ -0,0 +1,127 @@
use boa_interner::{Interner, Sym, ToInternedString};
use crate::syntax::ast::{function::Function, ContainsSymbol};
use super::Statement;
/// The set of AST nodes that can be preceded by a label, as defined by the [spec].
///
/// Semantically, a [`Labelled`] statement should only wrap [`Statement`] nodes. However,
/// old ECMAScript implementations supported [labelled function declarations][label-fn] as an extension
/// of the grammar. In the ECMAScript 2015 spec, the production of `LabelledStatement` was
/// modified to include labelled [`Function`]s as a valid node.
///
/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem
/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum LabelledItem {
Function(Function),
Statement(Statement),
}
impl LabelledItem {
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
match self {
LabelledItem::Function(f) => f.to_indented_string(interner, indentation),
LabelledItem::Statement(stmt) => stmt.to_indented_string(interner, indentation),
}
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
LabelledItem::Function(_) => false,
LabelledItem::Statement(stmt) => stmt.contains_arguments(),
}
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
LabelledItem::Function(_) => false,
LabelledItem::Statement(stmt) => stmt.contains(symbol),
}
}
}
impl ToInternedString for LabelledItem {
fn to_interned_string(&self, interner: &Interner) -> String {
self.to_indented_string(interner, 0)
}
}
impl From<Function> for LabelledItem {
fn from(f: Function) -> Self {
Self::Function(f)
}
}
impl From<Statement> for LabelledItem {
fn from(stmt: Statement) -> Self {
Self::Statement(stmt)
}
}
/// Labelled statement nodes, as defined by the [spec].
///
/// The method [`Labelled::item`] doesn't return a [`Statement`] for compatibility reasons.
/// See [`LabelledItem`] for more information.
///
/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Labelled {
item: Box<LabelledItem>,
label: Sym,
}
impl Labelled {
/// Creates a new `Labelled` statement.
pub fn new(item: LabelledItem, label: Sym) -> Self {
Self {
item: Box::new(item),
label,
}
}
/// Gets the labelled item.
pub fn item(&self) -> &LabelledItem {
&self.item
}
/// Gets the label name.
pub fn label(&self) -> Sym {
self.label
}
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"{}: {}",
interner.resolve_expect(self.label),
self.item.to_indented_string(interner, indentation)
)
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.label == Sym::ARGUMENTS || self.item.contains_arguments()
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.item.contains(symbol)
}
}
impl ToInternedString for Labelled {
fn to_interned_string(&self, interner: &Interner) -> String {
self.to_indented_string(interner, 0)
}
}
impl From<Labelled> for Statement {
fn from(labelled: Labelled) -> Self {
Self::Labelled(labelled)
}
}

45
boa_engine/src/syntax/ast/statement/mod.rs

@ -2,6 +2,7 @@
mod block; mod block;
mod r#if; mod r#if;
mod labelled;
mod r#return; mod r#return;
mod throw; mod throw;
@ -13,6 +14,7 @@ use self::iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer};
pub use self::{ pub use self::{
block::Block, block::Block,
iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop}, iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
labelled::{Labelled, LabelledItem},
r#if::If, r#if::If,
r#return::Return, r#return::Return,
r#try::{Catch, Finally, Try}, r#try::{Catch, Finally, Try},
@ -22,6 +24,7 @@ pub use self::{
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, ToInternedString};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use tap::Tap;
use super::{ use super::{
declaration::{Binding, VarDeclaration}, declaration::{Binding, VarDeclaration},
@ -84,8 +87,9 @@ pub enum Statement {
Return(Return), Return(Return),
// TODO: Possibly add `with` statements. // TODO: Possibly add `with` statements.
/// See [`Labelled`].
Labelled(Labelled),
// TODO: extract labels into a `LabelledStatement`
/// See [`Throw`]. /// See [`Throw`].
Throw(Throw), Throw(Throw),
@ -121,23 +125,27 @@ impl Statement {
/// indents, use [`to_indented_string()`](Self::to_indented_string). /// indents, use [`to_indented_string()`](Self::to_indented_string).
pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
match self { match self {
Self::Block(block) => block.to_indented_string(interner, indentation), Self::Block(block) => return block.to_indented_string(interner, indentation),
Self::Var(var) => var.to_interned_string(interner), Self::Var(var) => var.to_interned_string(interner),
Self::Empty => ";".to_owned(), Self::Empty => return ";".to_owned(),
Self::Expression(expr) => expr.to_indented_string(interner, indentation), Self::Expression(expr) => expr.to_indented_string(interner, indentation),
Self::If(if_smt) => if_smt.to_indented_string(interner, indentation), Self::If(if_smt) => return if_smt.to_indented_string(interner, indentation),
Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation), Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation),
Self::WhileLoop(while_loop) => while_loop.to_indented_string(interner, indentation), Self::WhileLoop(while_loop) => {
Self::ForLoop(for_loop) => for_loop.to_indented_string(interner, indentation), return while_loop.to_indented_string(interner, indentation)
Self::ForInLoop(for_in) => for_in.to_indented_string(interner, indentation), }
Self::ForOfLoop(for_of) => for_of.to_indented_string(interner, indentation), Self::ForLoop(for_loop) => return for_loop.to_indented_string(interner, indentation),
Self::Switch(switch) => switch.to_indented_string(interner, indentation), Self::ForInLoop(for_in) => return for_in.to_indented_string(interner, indentation),
Self::ForOfLoop(for_of) => return for_of.to_indented_string(interner, indentation),
Self::Switch(switch) => return switch.to_indented_string(interner, indentation),
Self::Continue(cont) => cont.to_interned_string(interner), Self::Continue(cont) => cont.to_interned_string(interner),
Self::Break(break_smt) => break_smt.to_interned_string(interner), Self::Break(break_smt) => break_smt.to_interned_string(interner),
Self::Return(ret) => ret.to_interned_string(interner), Self::Return(ret) => ret.to_interned_string(interner),
Self::Labelled(labelled) => return labelled.to_interned_string(interner),
Self::Throw(throw) => throw.to_interned_string(interner), Self::Throw(throw) => throw.to_interned_string(interner),
Self::Try(try_catch) => try_catch.to_indented_string(interner, indentation), Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),
} }
.tap_mut(|s| s.push(';'))
} }
pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet<Identifier>) { pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet<Identifier>) {
@ -218,6 +226,10 @@ impl Statement {
} }
} }
} }
Self::Labelled(labelled) => match labelled.item() {
LabelledItem::Function(_) => {}
LabelledItem::Statement(stmt) => stmt.var_declared_names(vars),
},
_ => {} _ => {}
} }
} }
@ -245,6 +257,7 @@ impl Statement {
Self::Continue(r#continue) => r#continue.contains_arguments(), Self::Continue(r#continue) => r#continue.contains_arguments(),
Self::Break(r#break) => r#break.contains_arguments(), Self::Break(r#break) => r#break.contains_arguments(),
Self::Return(r#return) => r#return.contains_arguments(), Self::Return(r#return) => r#return.contains_arguments(),
Self::Labelled(labelled) => labelled.contains_arguments(),
Self::Throw(throw) => throw.contains_arguments(), Self::Throw(throw) => throw.contains_arguments(),
Self::Try(r#try) => r#try.contains_arguments(), Self::Try(r#try) => r#try.contains_arguments(),
} }
@ -271,10 +284,22 @@ impl Statement {
Self::ForOfLoop(forof) => forof.contains(symbol), Self::ForOfLoop(forof) => forof.contains(symbol),
Self::Switch(switch) => switch.contains(symbol), Self::Switch(switch) => switch.contains(symbol),
Self::Return(r#return) => r#return.contains(symbol), Self::Return(r#return) => r#return.contains(symbol),
Self::Labelled(labelled) => labelled.contains(symbol),
Self::Throw(throw) => throw.contains(symbol), Self::Throw(throw) => throw.contains(symbol),
Self::Try(r#try) => r#try.contains(symbol), Self::Try(r#try) => r#try.contains(symbol),
} }
} }
#[inline]
pub(crate) fn is_labelled_function(&self) -> bool {
match self {
Self::Labelled(stmt) => match stmt.item() {
LabelledItem::Function(_) => true,
LabelledItem::Statement(stmt) => stmt.is_labelled_function(),
},
_ => false,
}
}
} }
impl ToInternedString for Statement { impl ToInternedString for Statement {

18
boa_engine/src/syntax/ast/statement/return.rs

@ -1,9 +1,5 @@
use crate::syntax::ast::{ use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
expression::{Expression, Identifier}, use boa_interner::{Interner, ToInternedString};
statement::Statement,
ContainsSymbol,
};
use boa_interner::{Interner, Sym, ToInternedString};
/// The `return` statement ends function execution and specifies a value to be returned to the /// The `return` statement ends function execution and specifies a value to be returned to the
/// function caller. /// function caller.
@ -27,26 +23,20 @@ use boa_interner::{Interner, Sym, ToInternedString};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Return { pub struct Return {
expr: Option<Expression>, expr: Option<Expression>,
label: Option<Identifier>,
} }
impl Return { impl Return {
pub fn label(&self) -> Option<Identifier> {
self.label
}
pub fn expr(&self) -> Option<&Expression> { pub fn expr(&self) -> Option<&Expression> {
self.expr.as_ref() self.expr.as_ref()
} }
/// Creates a `Return` AST node. /// Creates a `Return` AST node.
pub fn new(expr: Option<Expression>, label: Option<Identifier>) -> Self { pub fn new(expr: Option<Expression>) -> Self {
Self { expr, label } Self { expr }
} }
pub(crate) fn contains_arguments(&self) -> bool { pub(crate) fn contains_arguments(&self) -> bool {
matches!(self.expr, Some(ref expr) if expr.contains_arguments()) matches!(self.expr, Some(ref expr) if expr.contains_arguments())
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
} }
#[inline] #[inline]

14
boa_engine/src/syntax/ast/statement_list/mod.rs

@ -149,20 +149,6 @@ impl StatementList {
// We rely on the node to add the correct indent. // We rely on the node to add the correct indent.
buf.push_str(&item.to_indented_string(interner, indentation)); buf.push_str(&item.to_indented_string(interner, indentation));
match item {
StatementListItem::Statement(
Statement::Var(_)
| Statement::Expression(_)
| Statement::Continue(_)
| Statement::Break(_)
| Statement::Return(_)
| Statement::Throw(_)
| Statement::DoWhileLoop(_),
)
| StatementListItem::Declaration(Declaration::Lexical(_)) => buf.push(';'),
_ => {}
}
buf.push('\n'); buf.push('\n');
} }
buf buf

9
boa_engine/src/syntax/parser/error.rs

@ -110,6 +110,15 @@ impl ParseError {
} }
} }
/// Creates a "general" parsing error with the specific error message for a wrong function declaration with label.
#[inline]
pub(super) fn wrong_labelled_function_declaration(position: Position) -> Self {
Self::General {
message: "Labelled functions can only be declared at top level or inside a block",
position,
}
}
/// Creates a parsing error from a lexing error. /// Creates a parsing error from a lexing error.
pub(super) fn lex(e: LexError) -> Self { pub(super) fn lex(e: LexError) -> Self {
Self::Lex { err: e } Self::Lex { err: e }

1
boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs

@ -194,7 +194,6 @@ where
ExpressionBody::new(self.allow_in, false) ExpressionBody::new(self.allow_in, false)
.parse(cursor, interner)? .parse(cursor, interner)?
.into(), .into(),
None,
), ),
) )
.into()])), .into()])),

9
boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs

@ -30,7 +30,7 @@ fn check_async_expression() {
Some(add.into()), Some(add.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None), Return::new(Some(Literal::from(1).into())),
))] ))]
.into(), .into(),
) )
@ -71,10 +71,9 @@ fn check_nested_async_expression() {
AsyncFunction::new( AsyncFunction::new(
Some(b.into()), Some(b.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![Statement::Return(Return::new( vec![Statement::Return(Return::new(Some(
Some(Literal::from(1).into()), Literal::from(1).into(),
None, )))
))
.into()] .into()]
.into(), .into(),
) )

4
boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs

@ -33,7 +33,7 @@ fn check_async_generator_expr() {
Some(add.into()), Some(add.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None), Return::new(Some(Literal::from(1).into())),
))] ))]
.into(), .into(),
) )
@ -75,7 +75,7 @@ fn check_nested_async_generator_expr() {
Some(b.into()), Some(b.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None), Return::new(Some(Literal::from(1).into())),
))] ))]
.into(), .into(),
) )

6
boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs

@ -30,7 +30,7 @@ fn check_function_expression() {
Some(add.into()), Some(add.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None), Return::new(Some(Literal::from(1).into())),
))] ))]
.into(), .into(),
) )
@ -72,7 +72,7 @@ fn check_nested_function_expression() {
Some(b.into()), Some(b.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None), Return::new(Some(Literal::from(1).into())),
))] ))]
.into(), .into(),
) )
@ -107,7 +107,7 @@ fn check_function_non_reserved_keyword() {
Function::new( Function::new(
Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()), Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(Return::new(Some(Literal::from(1).into()), None)))].into(), vec![StatementListItem::Statement(Statement::Return(Return::new(Some(Literal::from(1).into()))))].into(),
) )
.into(), .into(),
), ),

155
boa_engine/src/syntax/parser/function/tests.rs

@ -42,10 +42,9 @@ fn check_basic() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
None, )),
),
))] ))]
.into(), .into(),
)) ))
@ -84,10 +83,9 @@ fn check_duplicates_strict_off() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
None, )),
),
))] ))]
.into(), .into(),
)) ))
@ -126,10 +124,9 @@ fn check_basic_semicolon_insertion() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
None, )),
),
))] ))]
.into(), .into(),
)) ))
@ -158,7 +155,7 @@ fn check_empty_return() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(None, None), Return::new(None),
))] ))]
.into(), .into(),
)) ))
@ -187,7 +184,7 @@ fn check_empty_return_semicolon_insertion() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(None, None), Return::new(None),
))] ))]
.into(), .into(),
)) ))
@ -331,17 +328,14 @@ fn check_arrow() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Binary::new(
Binary::new( ArithmeticOp::Add.into(),
ArithmeticOp::Add.into(), Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(),
Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(),
Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), )
) .into(),
.into(), )),
),
None,
),
))] ))]
.into(), .into(),
))) )))
@ -379,17 +373,14 @@ fn check_arrow_semicolon_insertion() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Binary::new(
Binary::new( ArithmeticOp::Add.into(),
ArithmeticOp::Add.into(), Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(),
Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(),
Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), )
) .into(),
.into(), )),
),
None,
),
))] ))]
.into(), .into(),
))) )))
@ -427,7 +418,7 @@ fn check_arrow_epty_return() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(None, None), Return::new(None),
))] ))]
.into(), .into(),
))) )))
@ -465,7 +456,7 @@ fn check_arrow_empty_return_semicolon_insertion() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(None, None), Return::new(None),
))] ))]
.into(), .into(),
))) )))
@ -497,15 +488,10 @@ fn check_arrow_assignment() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -543,15 +529,10 @@ fn check_arrow_assignment_nobrackets() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -589,15 +570,10 @@ fn check_arrow_assignment_noparenthesis() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -635,15 +611,10 @@ fn check_arrow_assignment_noparenthesis_nobrackets() {
length: 1, length: 1,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -690,15 +661,10 @@ fn check_arrow_assignment_2arg() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -745,15 +711,10 @@ fn check_arrow_assignment_2arg_nobrackets() {
length: 2, length: 2,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -807,15 +768,10 @@ fn check_arrow_assignment_3arg() {
length: 3, length: 3,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )
@ -869,15 +825,10 @@ fn check_arrow_assignment_3arg_nobrackets() {
length: 3, length: 3,
}, },
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new( Return::new(Some(
Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
Identifier::new(
interner.get_or_intern_static("a", utf16!("a")),
)
.into(), .into(),
), )),
None,
),
))] ))]
.into(), .into(),
) )

4
boa_engine/src/syntax/parser/statement/block/tests.rs

@ -83,7 +83,7 @@ fn non_empty() {
Some(hello.into()), Some(hello.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(10).into()), None), Return::new(Some(Literal::from(10).into())),
))] ))]
.into(), .into(),
)) ))
@ -124,7 +124,7 @@ fn hoisting() {
Some(hello.into()), Some(hello.into()),
FormalParameterList::default(), FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return( vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(10).into()), None), Return::new(Some(Literal::from(10).into())),
))] ))]
.into(), .into(),
)) ))

55
boa_engine/src/syntax/parser/statement/if_stm/mod.rs

@ -95,7 +95,12 @@ where
.parse(cursor, interner)?, .parse(cursor, interner)?,
}; };
let else_node = if let Some(token) = cursor.peek(0, interner)? { // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true.
if then_node.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
let else_stmt = if let Some(token) = cursor.peek(0, interner)? {
match token.kind() { match token.kind() {
TokenKind::Keyword((Keyword::Else, true)) => { TokenKind::Keyword((Keyword::Else, true)) => {
return Err(ParseError::general( return Err(ParseError::general(
@ -108,7 +113,8 @@ where
let strict = cursor.strict_mode(); let strict = cursor.strict_mode();
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match token.kind() { let position = token.span().start();
let stmt = match token.kind() {
TokenKind::Keyword((Keyword::Function, _)) => { TokenKind::Keyword((Keyword::Function, _)) => {
// FunctionDeclarations in IfStatement Statement Clauses // FunctionDeclarations in IfStatement Statement Clauses
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses // https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
@ -118,28 +124,31 @@ where
)); ));
} }
Some( // Source text matched by this production is processed as if each matching
// Source text matched by this production is processed as if each matching // occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
// occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole // StatementListItem of a BlockStatement occupying that position in the source text.
// StatementListItem of a BlockStatement occupying that position in the source text. Block::from(vec![StatementListItem::Declaration(
Block::from(vec![StatementListItem::Declaration( Declaration::Function(
Declaration::Function( FunctionDeclaration::new(
FunctionDeclaration::new( self.allow_yield,
self.allow_yield, self.allow_await,
self.allow_await, false,
false, )
) .parse(cursor, interner)?,
.parse(cursor, interner)?, ),
), )])
)]) .into()
.into(),
)
} }
_ => Some( _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)
Statement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner)?,
.parse(cursor, interner)?, };
),
// Early Error: It is a Syntax Error if IsLabelledFunction(the second Statement) is true.
if stmt.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
} }
Some(stmt)
} }
_ => None, _ => None,
} }
@ -147,6 +156,6 @@ where
None None
}; };
Ok(If::new(condition, then_node, else_node)) Ok(If::new(condition, then_node, else_stmt))
} }
} }

11
boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs

@ -65,9 +65,20 @@ where
cursor.expect((Keyword::Do, false), "do while statement", interner)?; cursor.expect((Keyword::Do, false), "do while statement", interner)?;
let position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return) let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?; .parse(cursor, interner)?;
// Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.
if body.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match next_token.kind() { match next_token.kind() {
TokenKind::Keyword((Keyword::While, true)) => { TokenKind::Keyword((Keyword::While, true)) => {

29
boa_engine/src/syntax/parser/statement/iteration/for_statement.rs

@ -167,14 +167,22 @@ where
let expr = Expression::new(None, true, self.allow_yield, self.allow_await) let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "for in statement", interner)?;
let position = cursor let position = cursor
.expect(Punctuator::CloseParen, "for in statement", interner)? .peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span() .span()
.end(); .start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return) let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?; .parse(cursor, interner)?;
// Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.
if body.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
// It is a Syntax Error if the BoundNames of ForDeclaration contains "let". // It is a Syntax Error if the BoundNames of ForDeclaration contains "let".
// It is a Syntax Error if any element of the BoundNames of ForDeclaration also occurs in the VarDeclaredNames of Statement. // It is a Syntax Error if any element of the BoundNames of ForDeclaration also occurs in the VarDeclaredNames of Statement.
// It is a Syntax Error if the BoundNames of ForDeclaration contains any duplicate entries. // It is a Syntax Error if the BoundNames of ForDeclaration contains any duplicate entries.
@ -212,14 +220,22 @@ where
let iterable = Expression::new(None, true, self.allow_yield, self.allow_await) let iterable = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "for of statement", interner)?;
let position = cursor let position = cursor
.expect(Punctuator::CloseParen, "for of statement", interner)? .peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span() .span()
.end(); .start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return) let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?; .parse(cursor, interner)?;
// Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.
if body.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
// It is a Syntax Error if the BoundNames of ForDeclaration contains "let". // It is a Syntax Error if the BoundNames of ForDeclaration contains "let".
// It is a Syntax Error if any element of the BoundNames of ForDeclaration also occurs in the VarDeclaredNames of Statement. // It is a Syntax Error if any element of the BoundNames of ForDeclaration also occurs in the VarDeclaredNames of Statement.
// It is a Syntax Error if the BoundNames of ForDeclaration contains any duplicate entries. // It is a Syntax Error if the BoundNames of ForDeclaration contains any duplicate entries.
@ -299,6 +315,11 @@ where
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return) let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?; .parse(cursor, interner)?;
// Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.
if body.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
// Early Error: It is a Syntax Error if any element of the BoundNames of // Early Error: It is a Syntax Error if any element of the BoundNames of
// LexicalDeclaration also occurs in the VarDeclaredNames of Statement. // LexicalDeclaration also occurs in the VarDeclaredNames of Statement.
let mut vars = FxHashSet::default(); let mut vars = FxHashSet::default();

13
boa_engine/src/syntax/parser/statement/iteration/while_statement.rs

@ -2,7 +2,7 @@ use crate::syntax::{
ast::{statement::WhileLoop, Keyword, Punctuator}, ast::{statement::WhileLoop, Keyword, Punctuator},
parser::{ parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
ParseResult, TokenParser, ParseError, ParseResult, TokenParser,
}, },
}; };
use boa_interner::Interner; use boa_interner::Interner;
@ -61,9 +61,20 @@ where
cursor.expect(Punctuator::CloseParen, "while statement", interner)?; cursor.expect(Punctuator::CloseParen, "while statement", interner)?;
let position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return) let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?; .parse(cursor, interner)?;
// Early Error: It is a Syntax Error if IsLabelledFunction(the second Statement) is true.
if body.is_labelled_function() {
return Err(ParseError::wrong_labelled_function_declaration(position));
}
Ok(WhileLoop::new(cond, body)) Ok(WhileLoop::new(cond, body))
} }
} }

40
boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs

@ -9,10 +9,12 @@ use crate::syntax::{
AllowYield, ParseResult, TokenParser, AllowYield, ParseResult, TokenParser,
}, },
}; };
use boa_interner::{Interner, Sym}; use boa_interner::Interner;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
use super::declaration::FunctionDeclaration;
/// Labelled Statement Parsing /// Labelled Statement Parsing
/// ///
/// More information /// More information
@ -47,12 +49,12 @@ impl<R> TokenParser<R> for LabelledStatement
where where
R: Read, R: Read,
{ {
type Output = ast::Statement; type Output = ast::statement::Labelled;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("Label", "Parsing"); let _timer = Profiler::global().start_event("Label", "Parsing");
let name = LabelIdentifier::new(self.allow_yield, self.allow_await) let label = LabelIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)? .parse(cursor, interner)?
.sym(); .sym();
@ -60,9 +62,8 @@ where
let strict = cursor.strict_mode(); let strict = cursor.strict_mode();
let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
// TODO: create `ast::Statement::Labelled`
Ok(match next_token.kind() { let labelled_item = match next_token.kind() {
// Early Error: It is a Syntax Error if any strict mode source code matches this rule. // Early Error: It is a Syntax Error if any strict mode source code matches this rule.
// https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-early-errors // https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-early-errors
// https://tc39.es/ecma262/#sec-labelled-function-declarations // https://tc39.es/ecma262/#sec-labelled-function-declarations
@ -72,29 +73,14 @@ where
next_token.span().start() next_token.span().start()
)) ))
} }
// TODO: temporarily disable until we implement `LabelledStatement` TokenKind::Keyword((Keyword::Function, _)) => {
// TokenKind::Keyword((Keyword::Function, _)) => { FunctionDeclaration::new(self.allow_yield, self.allow_await, false)
// Declaration::Function(FunctionDeclaration::new(self.allow_yield, self.allow_await, false) .parse(cursor, interner)?
// .parse(cursor, interner)?) .into()
// .into()
// }
_ => {
let mut stmt = Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor, interner)?;
set_label_for_node(&mut stmt, name);
stmt
} }
}) _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor, interner)?.into()
} };
}
fn set_label_for_node(node: &mut ast::Statement, name: Sym) { Ok(ast::statement::Labelled::new(labelled_item, label))
match node {
ast::Statement::ForLoop(ref mut for_loop) => for_loop.set_label(name),
ast::Statement::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name),
ast::Statement::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name),
ast::Statement::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name),
ast::Statement::WhileLoop(ref mut while_loop) => while_loop.set_label(name),
ast::Statement::Block(ref mut block) => block.set_label(name),
_ => (),
} }
} }

4
boa_engine/src/syntax/parser/statement/return_stm/mod.rs

@ -57,7 +57,7 @@ where
_ => {} _ => {}
} }
return Ok(Return::new(None, None)); return Ok(Return::new(None));
} }
let expr = Expression::new(None, true, self.allow_yield, self.allow_await) let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
@ -65,6 +65,6 @@ where
cursor.expect_semicolon("return statement", interner)?; cursor.expect_semicolon("return statement", interner)?;
Ok(Return::new(Some(expr), None)) Ok(Return::new(Some(expr)))
} }
} }

3
boa_engine/src/syntax/parser/tests.rs

@ -114,8 +114,7 @@ fn hoisting() {
Declaration::Function(Function::new( Declaration::Function(Function::new(
Some(hello), Some(hello),
FormalParameterList::default(), FormalParameterList::default(),
vec![Statement::Return(Return::new(Some(Literal::from(10).into()), None)).into()] vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()].into(),
.into(),
)) ))
.into(), .into(),
Statement::Var(VarDeclaration( Statement::Var(VarDeclaration(

Loading…
Cancel
Save