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. 228
      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. 131
      boa_engine/src/syntax/parser/function/tests.rs
  19. 4
      boa_engine/src/syntax/parser/statement/block/tests.rs
  20. 27
      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

228
boa_engine/src/bytecompiler/mod.rs

@ -20,7 +20,10 @@ use crate::{
},
pattern::{Pattern, PatternArrayElement, PatternObjectElement},
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,
},
vm::{BindingOpcode, CodeBlock, Opcode},
@ -1553,31 +1556,9 @@ impl<'b> ByteCompiler<'b> {
}
#[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) => {
pub fn compile_for_loop(&mut self, for_loop: &ForLoop, label: Option<Sym>) -> JsResult<()> {
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
if let Some(init) = for_loop.init() {
match init {
@ -1597,7 +1578,7 @@ impl<'b> ByteCompiler<'b> {
let initial_jump = self.jump();
let start_address = self.next_opcode_location();
self.push_loop_control_info(for_loop.label(), start_address);
self.push_loop_control_info(label, start_address);
self.emit_opcode(Opcode::LoopContinue);
if let Some(final_expr) = for_loop.final_expr() {
@ -1621,31 +1602,34 @@ impl<'b> ByteCompiler<'b> {
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
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);
self.emit_opcode(Opcode::PopEnvironment);
Ok(())
}
Statement::ForInLoop(for_in_loop) => {
#[inline]
pub fn compile_for_in_loop(
&mut self,
for_in_loop: &ForInLoop,
label: Option<Sym>,
) -> JsResult<()> {
let init_bound_names = for_in_loop.init().bound_names();
if init_bound_names.is_empty() {
self.compile_expr(for_in_loop.expr(), true)?;
} else {
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names {
self.context.create_mutable_binding(name, false);
}
self.compile_expr(for_in_loop.expr(), true)?;
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
let index_compile_environment =
self.push_compile_environment(compile_environment);
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);
self.emit_opcode(Opcode::PopEnvironment);
@ -1655,12 +1639,11 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::LoopStart);
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.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext);
match for_in_loop.init() {
@ -1716,8 +1699,7 @@ impl<'b> ByteCompiler<'b> {
self.compile_stmt(for_in_loop.body(), false)?;
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
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);
@ -1731,25 +1713,29 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::IteratorClose);
self.patch_jump(early_exit);
Ok(())
}
Statement::ForOfLoop(for_of_loop) => {
#[inline]
pub fn compile_for_of_loop(
&mut self,
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 push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
for name in init_bound_names {
self.context.create_mutable_binding(name, false);
}
self.compile_expr(for_of_loop.iterable(), true)?;
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
let index_compile_environment =
self.push_compile_environment(compile_environment);
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);
self.emit_opcode(Opcode::PopEnvironment);
@ -1763,12 +1749,11 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info_for_of_in_loop(for_of_loop.label(), start_address);
self.push_loop_control_info_for_of_in_loop(label, start_address);
self.emit_opcode(Opcode::LoopContinue);
self.context.push_compile_time_environment(false);
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
let exit = if for_of_loop.r#await() {
self.emit_opcode(Opcode::ForAwaitOfLoopIterate);
@ -1832,8 +1817,7 @@ impl<'b> ByteCompiler<'b> {
self.compile_stmt(for_of_loop.body(), false)?;
let (num_bindings, compile_environment) =
self.context.pop_compile_time_environment();
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);
@ -1845,43 +1829,144 @@ impl<'b> ByteCompiler<'b> {
self.pop_loop_control_info();
self.emit_opcode(Opcode::LoopEnd);
self.emit_opcode(Opcode::IteratorClose);
Ok(())
}
Statement::WhileLoop(while_) => {
#[inline]
pub fn compile_while_loop(
&mut self,
while_loop: &WhileLoop,
label: Option<Sym>,
) -> JsResult<()> {
self.emit_opcode(Opcode::LoopStart);
let start_address = self.next_opcode_location();
self.push_loop_control_info(while_.label(), start_address);
self.push_loop_control_info(label, start_address);
self.emit_opcode(Opcode::LoopContinue);
self.compile_expr(while_.condition(), true)?;
self.compile_expr(while_loop.condition(), true)?;
let exit = self.jump_if_false();
self.compile_stmt(while_.body(), 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(())
}
Statement::DoWhileLoop(do_while) => {
#[inline]
pub fn compile_do_while_loop(
&mut self,
do_while_loop: &DoWhileLoop,
label: Option<Sym>,
) -> JsResult<()> {
self.emit_opcode(Opcode::LoopStart);
let initial_label = self.jump();
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);
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();
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.patch_jump(exit);
self.pop_loop_control_info();
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) => {
let next = self.next_opcode_location();
if let Some(info) = self
@ -2020,29 +2105,6 @@ impl<'b> ByteCompiler<'b> {
.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) => {
self.compile_expr(throw.expr(), true)?;
self.emit(Opcode::Throw, &[]);

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

@ -1,4 +1,5 @@
use boa_interner::{Interner, ToInternedString};
use tap::Tap;
use super::{
expression::Identifier,
@ -40,7 +41,7 @@ impl Declaration {
Declaration::AsyncFunction(af) => af.to_indented_string(interner, indentation),
Declaration::AsyncGenerator(ag) => ag.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 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
/// more statements.
@ -25,7 +25,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct Block {
#[cfg_attr(feature = "deser", serde(flatten))]
statements: StatementList,
label: Option<Sym>,
}
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]
pub(crate) fn contains_arguments(&self) -> bool {
self.statements.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.statements.contains(symbol)
}
@ -75,7 +66,6 @@ where
fn from(list: T) -> Self {
Self {
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 boa_interner::{Interner, Sym, ToInternedString};
use boa_interner::{Interner, ToInternedString};
/// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false.
@ -18,7 +18,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct DoWhileLoop {
body: Box<Statement>,
condition: Expression,
label: Option<Sym>,
}
impl DoWhileLoop {
@ -29,45 +28,26 @@ impl DoWhileLoop {
pub fn cond(&self) -> &Expression {
&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.
pub fn new(body: Statement, condition: Expression) -> Self {
Self {
body: body.into(),
condition,
label: None,
}
}
/// Converts the "do while" loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label {
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
format!(
"do {} while ({})",
self.body().to_indented_string(interner, indentation),
self.cond().to_interned_string(interner)
));
buf
)
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.body.contains_arguments()
|| self.condition.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
self.body.contains_arguments() || self.condition.contains_arguments()
}
#[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},
ContainsSymbol,
};
use boa_interner::{Interner, Sym, ToInternedString};
use boa_interner::{Interner, ToInternedString};
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForInLoop {
init: IterableLoopInitializer,
expr: Expression,
body: Box<Statement>,
label: Option<Sym>,
}
impl ForInLoop {
@ -19,7 +18,6 @@ impl ForInLoop {
init,
expr,
body: body.into(),
label: None,
}
}
@ -35,26 +33,13 @@ impl ForInLoop {
&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.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label {
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
let mut buf = format!(
"for ({} in {}) ",
self.init.to_interned_string(interner),
self.expr.to_interned_string(interner)
));
);
buf.push_str(&self.body().to_indented_string(interner, indentation));
buf
@ -65,7 +50,6 @@ impl ForInLoop {
self.init.contains_arguments()
|| self.expr.contains_arguments()
|| self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
#[inline]

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

@ -4,7 +4,7 @@ use crate::syntax::ast::{
statement::Statement,
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.
///
@ -22,7 +22,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct ForLoop {
#[cfg_attr(feature = "deser", serde(flatten))]
inner: Box<InnerForLoop>,
label: Option<Sym>,
}
impl ForLoop {
@ -35,7 +34,6 @@ impl ForLoop {
) -> Self {
Self {
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.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label {
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str("for (");
let mut buf = String::from("for (");
if let Some(init) = self.init() {
buf.push_str(&init.to_interned_string(interner));
}
@ -86,14 +79,6 @@ impl ForLoop {
buf
}
pub fn label(&self) -> Option<Sym> {
self.label
}
pub fn set_label(&mut self, label: Sym) {
self.label = Some(label);
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
let inner = &self.inner;
@ -101,7 +86,6 @@ impl ForLoop {
|| matches!(inner.condition, Some(ref expr) if expr.contains_arguments())
|| matches!(inner.final_expr, Some(ref expr) if expr.contains_arguments())
|| inner.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
#[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},
ContainsSymbol,
};
use boa_interner::{Interner, Sym, ToInternedString};
use boa_interner::{Interner, ToInternedString};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
@ -14,7 +14,6 @@ pub struct ForOfLoop {
init: IterableLoopInitializer,
iterable: Expression,
body: Box<Statement>,
label: Option<Sym>,
r#await: bool,
}
@ -30,7 +29,6 @@ impl ForOfLoop {
init,
iterable,
body: body.into(),
label: None,
r#await,
}
}
@ -47,14 +45,6 @@ impl ForOfLoop {
&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.
pub(crate) fn r#await(&self) -> bool {
self.r#await
@ -62,19 +52,12 @@ impl ForOfLoop {
/// Converts the "for of" loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label {
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
format!(
"for ({} of {}) {}",
self.init.to_interned_string(interner),
self.iterable.to_interned_string(interner),
self.body().to_indented_string(interner, indentation)
));
buf
)
}
#[inline]
@ -82,7 +65,6 @@ impl ForOfLoop {
self.init.contains_arguments()
|| self.iterable.contains_arguments()
|| self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
}
#[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 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
/// test condition evaluates to `true`.
///
@ -16,7 +16,6 @@ use boa_interner::{Interner, Sym, ToInternedString};
pub struct WhileLoop {
condition: Expression,
body: Box<Statement>,
label: Option<Sym>,
}
impl WhileLoop {
@ -27,44 +26,25 @@ impl WhileLoop {
pub fn body(&self) -> &Statement {
&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.
pub fn new(condition: Expression, body: Statement) -> Self {
Self {
condition,
body: body.into(),
label: None,
}
}
/// Converts the while loop to a string with the given indentation.
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = if let Some(label) = self.label {
format!("{}: ", interner.resolve_expect(label))
} else {
String::new()
};
buf.push_str(&format!(
format!(
"while ({}) {}",
self.condition().to_interned_string(interner),
self.body().to_indented_string(interner, indentation)
));
buf
)
}
pub(crate) fn contains_arguments(&self) -> bool {
self.condition.contains_arguments()
|| self.body.contains_arguments()
|| matches!(self.label, Some(label) if label == Sym::ARGUMENTS)
self.condition.contains_arguments() || self.body.contains_arguments()
}
#[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 r#if;
mod labelled;
mod r#return;
mod throw;
@ -13,6 +14,7 @@ use self::iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer};
pub use self::{
block::Block,
iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
labelled::{Labelled, LabelledItem},
r#if::If,
r#return::Return,
r#try::{Catch, Finally, Try},
@ -22,6 +24,7 @@ pub use self::{
use boa_interner::{Interner, ToInternedString};
use rustc_hash::FxHashSet;
use tap::Tap;
use super::{
declaration::{Binding, VarDeclaration},
@ -84,8 +87,9 @@ pub enum Statement {
Return(Return),
// TODO: Possibly add `with` statements.
/// See [`Labelled`].
Labelled(Labelled),
// TODO: extract labels into a `LabelledStatement`
/// See [`Throw`].
Throw(Throw),
@ -121,23 +125,27 @@ impl Statement {
/// indents, use [`to_indented_string()`](Self::to_indented_string).
pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
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::Empty => ";".to_owned(),
Self::Empty => return ";".to_owned(),
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::WhileLoop(while_loop) => while_loop.to_indented_string(interner, indentation),
Self::ForLoop(for_loop) => for_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::Switch(switch) => switch.to_indented_string(interner, indentation),
Self::WhileLoop(while_loop) => {
return while_loop.to_indented_string(interner, indentation)
}
Self::ForLoop(for_loop) => return for_loop.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::Break(break_smt) => break_smt.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::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>) {
@ -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::Break(r#break) => r#break.contains_arguments(),
Self::Return(r#return) => r#return.contains_arguments(),
Self::Labelled(labelled) => labelled.contains_arguments(),
Self::Throw(throw) => throw.contains_arguments(),
Self::Try(r#try) => r#try.contains_arguments(),
}
@ -271,10 +284,22 @@ impl Statement {
Self::ForOfLoop(forof) => forof.contains(symbol),
Self::Switch(switch) => switch.contains(symbol),
Self::Return(r#return) => r#return.contains(symbol),
Self::Labelled(labelled) => labelled.contains(symbol),
Self::Throw(throw) => throw.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 {

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

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

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.
pub(super) fn lex(e: LexError) -> Self {
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)
.parse(cursor, interner)?
.into(),
None,
),
)
.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()),
FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None),
Return::new(Some(Literal::from(1).into())),
))]
.into(),
)
@ -71,10 +71,9 @@ fn check_nested_async_expression() {
AsyncFunction::new(
Some(b.into()),
FormalParameterList::default(),
vec![Statement::Return(Return::new(
Some(Literal::from(1).into()),
None,
))
vec![Statement::Return(Return::new(Some(
Literal::from(1).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()),
FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None),
Return::new(Some(Literal::from(1).into())),
))]
.into(),
)
@ -75,7 +75,7 @@ fn check_nested_async_generator_expr() {
Some(b.into()),
FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None),
Return::new(Some(Literal::from(1).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()),
FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None),
Return::new(Some(Literal::from(1).into())),
))]
.into(),
)
@ -72,7 +72,7 @@ fn check_nested_function_expression() {
Some(b.into()),
FormalParameterList::default(),
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(Literal::from(1).into()), None),
Return::new(Some(Literal::from(1).into())),
))]
.into(),
)
@ -107,7 +107,7 @@ fn check_function_non_reserved_keyword() {
Function::new(
Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()),
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(),
),

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

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

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

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

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

@ -95,7 +95,12 @@ where
.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() {
TokenKind::Keyword((Keyword::Else, true)) => {
return Err(ParseError::general(
@ -108,7 +113,8 @@ where
let strict = cursor.strict_mode();
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, _)) => {
// FunctionDeclarations in IfStatement Statement Clauses
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
@ -118,7 +124,6 @@ where
));
}
Some(
// Source text matched by this production is processed as if each matching
// occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
// StatementListItem of a BlockStatement occupying that position in the source text.
@ -132,14 +137,18 @@ where
.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)?,
),
};
// 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,
}
@ -147,6 +156,6 @@ where
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)?;
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)
.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)?;
match next_token.kind() {
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)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "for in statement", interner)?;
let position = cursor
.expect(Punctuator::CloseParen, "for in statement", interner)?
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.end();
.start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.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 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.
@ -212,14 +220,22 @@ where
let iterable = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "for of statement", interner)?;
let position = cursor
.expect(Punctuator::CloseParen, "for of statement", interner)?
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.end();
.start();
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.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 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.
@ -299,6 +315,11 @@ where
let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
.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
// LexicalDeclaration also occurs in the VarDeclaredNames of Statement.
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},
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
ParseResult, TokenParser,
ParseError, ParseResult, TokenParser,
},
};
use boa_interner::Interner;
@ -61,9 +61,20 @@ where
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)
.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))
}
}

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

@ -9,10 +9,12 @@ use crate::syntax::{
AllowYield, ParseResult, TokenParser,
},
};
use boa_interner::{Interner, Sym};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
use super::declaration::FunctionDeclaration;
/// Labelled Statement Parsing
///
/// More information
@ -47,12 +49,12 @@ impl<R> TokenParser<R> for LabelledStatement
where
R: Read,
{
type Output = ast::Statement;
type Output = ast::statement::Labelled;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
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)?
.sym();
@ -60,9 +62,8 @@ where
let strict = cursor.strict_mode();
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.
// https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-early-errors
// https://tc39.es/ecma262/#sec-labelled-function-declarations
@ -72,29 +73,14 @@ where
next_token.span().start()
))
}
// TODO: temporarily disable until we implement `LabelledStatement`
// TokenKind::Keyword((Keyword::Function, _)) => {
// Declaration::Function(FunctionDeclaration::new(self.allow_yield, self.allow_await, false)
// .parse(cursor, interner)?)
// .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
}
})
TokenKind::Keyword((Keyword::Function, _)) => {
FunctionDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor, interner)?
.into()
}
}
_ => 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) {
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),
_ => (),
Ok(ast::statement::Labelled::new(labelled_item, label))
}
}

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)
@ -65,6 +65,6 @@ where
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(
Some(hello),
FormalParameterList::default(),
vec![Statement::Return(Return::new(Some(Literal::from(10).into()), None)).into()]
.into(),
vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()].into(),
))
.into(),
Statement::Var(VarDeclaration(

Loading…
Cancel
Save