From d4c220a70b5879ff989b1cfbd48259aa565669a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Sat, 22 Oct 2022 12:38:51 +0000 Subject: [PATCH] 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. --- boa_engine/src/bytecompiler/mod.rs | 670 ++++++++++-------- boa_engine/src/syntax/ast/declaration/mod.rs | 3 +- boa_engine/src/syntax/ast/statement/block.rs | 14 +- .../ast/statement/iteration/do_while_loop.rs | 28 +- .../ast/statement/iteration/for_in_loop.rs | 22 +- .../ast/statement/iteration/for_loop.rs | 20 +- .../ast/statement/iteration/for_of_loop.rs | 24 +- .../ast/statement/iteration/while_loop.rs | 28 +- .../src/syntax/ast/statement/labelled.rs | 127 ++++ boa_engine/src/syntax/ast/statement/mod.rs | 45 +- boa_engine/src/syntax/ast/statement/return.rs | 18 +- .../src/syntax/ast/statement_list/mod.rs | 14 - boa_engine/src/syntax/parser/error.rs | 9 + .../expression/assignment/arrow_function.rs | 1 - .../async_function_expression/tests.rs | 9 +- .../async_generator_expression/tests.rs | 4 +- .../primary/function_expression/tests.rs | 6 +- .../src/syntax/parser/function/tests.rs | 155 ++-- .../syntax/parser/statement/block/tests.rs | 4 +- .../src/syntax/parser/statement/if_stm/mod.rs | 55 +- .../statement/iteration/do_while_statement.rs | 11 + .../statement/iteration/for_statement.rs | 29 +- .../statement/iteration/while_statement.rs | 13 +- .../parser/statement/labelled_stm/mod.rs | 40 +- .../syntax/parser/statement/return_stm/mod.rs | 4 +- boa_engine/src/syntax/parser/tests.rs | 3 +- 26 files changed, 721 insertions(+), 635 deletions(-) create mode 100644 boa_engine/src/syntax/ast/statement/labelled.rs diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 3709719fc5..560c6beb0f 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/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,335 +1556,417 @@ 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)?; + pub fn compile_for_loop(&mut self, for_loop: &ForLoop, label: Option) -> JsResult<()> { + self.context.push_compile_time_environment(false); + let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - 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); - } + if let Some(init) = for_loop.init() { + match init { + 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)?; } } - 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() { - match init { - 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 initial_jump = self.jump(); - self.emit_opcode(Opcode::LoopStart); - let initial_jump = self.jump(); + let start_address = self.next_opcode_location(); + self.push_loop_control_info(label, start_address); - let start_address = self.next_opcode_location(); - self.push_loop_control_info(for_loop.label(), start_address); + self.emit_opcode(Opcode::LoopContinue); + if let Some(final_expr) = for_loop.final_expr() { + self.compile_expr(final_expr, false)?; + } - self.emit_opcode(Opcode::LoopContinue); - if let Some(final_expr) = for_loop.final_expr() { - self.compile_expr(final_expr, false)?; - } + self.patch_jump(initial_jump); - 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_expr(condition, true)?; - } else { - self.emit_opcode(Opcode::PushTrue); - } - let exit = self.jump_if_false(); + self.compile_stmt(for_loop.body(), 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); - self.pop_loop_control_info(); - self.emit_opcode(Opcode::LoopEnd); + 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(()) + } - 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); - } - Statement::ForInLoop(for_in_loop) => { - 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); + #[inline] + pub fn compile_for_in_loop( + &mut self, + for_in_loop: &ForInLoop, + label: Option, + ) -> 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); - for name in init_bound_names { - self.context.create_mutable_binding(name, false); - } - self.compile_expr(for_in_loop.expr(), true)?; + 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); - 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); - } + 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); + } - 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); - let start_address = self.next_opcode_location(); - self.push_loop_control_info_for_of_in_loop(for_in_loop.label(), start_address); - self.emit_opcode(Opcode::LoopContinue); + self.emit_opcode(Opcode::LoopStart); + let start_address = self.next_opcode_location(); + 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 exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext); + self.context.push_compile_time_environment(false); + 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() { - IterableLoopInitializer::Identifier(ident) => { - self.context.create_mutable_binding(*ident, true); - let binding = self.context.set_mutable_binding(*ident); - let index = self.get_or_insert_binding(binding); - self.emit(Opcode::DefInitVar, &[index]); + match for_in_loop.init() { + IterableLoopInitializer::Identifier(ident) => { + self.context.create_mutable_binding(*ident, true); + let binding = self.context.set_mutable_binding(*ident); + let index = self.get_or_insert_binding(binding); + 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 { - 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); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; - } - }, - IterableLoopInitializer::Let(declaration) => match declaration { - Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLet, *ident); - } - Binding::Pattern(pattern) => { - for ident in pattern.idents() { - self.context.create_mutable_binding(ident, false); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; - } - }, - 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::InitVar)?; + } + }, + IterableLoopInitializer::Let(declaration) => match declaration { + Binding::Identifier(ident) => { + self.context.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLet, *ident); + } + Binding::Pattern(pattern) => { + for ident in pattern.idents() { + self.context.create_mutable_binding(ident, false); + } + self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; + } + }, + 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_stmt(for_in_loop.body(), false)?; + self.compile_stmt(for_in_loop.body(), false)?; - 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); + 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); - 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.emit_opcode(Opcode::IteratorClose); + self.patch_jump(exit); + self.pop_loop_control_info(); + self.emit_opcode(Opcode::LoopEnd); + self.emit_opcode(Opcode::IteratorClose); - self.patch_jump(early_exit); - } - 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); + self.patch_jump(early_exit); + Ok(()) + } - for name in init_bound_names { - self.context.create_mutable_binding(name, false); - } - self.compile_expr(for_of_loop.iterable(), true)?; + #[inline] + pub fn compile_for_of_loop( + &mut self, + for_of_loop: &ForOfLoop, + label: Option, + ) -> 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) = - 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); - } + for name in init_bound_names { + self.context.create_mutable_binding(name, false); + } + self.compile_expr(for_of_loop.iterable(), true)?; - if for_of_loop.r#await() { - self.emit_opcode(Opcode::InitIteratorAsync); - } else { - self.emit_opcode(Opcode::InitIterator); - } + 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); + } - 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.emit_opcode(Opcode::LoopContinue); + if for_of_loop.r#await() { + self.emit_opcode(Opcode::InitIteratorAsync); + } else { + self.emit_opcode(Opcode::InitIterator); + } - self.context.push_compile_time_environment(false); - let push_env = - self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); + self.emit_opcode(Opcode::LoopStart); + let start_address = self.next_opcode_location(); + 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.emit_opcode(Opcode::ForAwaitOfLoopIterate); - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::GeneratorNext); - self.emit_opcode_with_operand(Opcode::ForAwaitOfLoopNext) - } else { - self.emit_opcode_with_operand(Opcode::ForInLoopNext) - }; + self.context.push_compile_time_environment(false); + let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); + + let exit = if for_of_loop.r#await() { + self.emit_opcode(Opcode::ForAwaitOfLoopIterate); + self.emit_opcode(Opcode::Await); + 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() { - IterableLoopInitializer::Identifier(ref ident) => { - self.context.create_mutable_binding(*ident, true); - let binding = self.context.set_mutable_binding(*ident); - let index = self.get_or_insert_binding(binding); - self.emit(Opcode::DefInitVar, &[index]); + match for_of_loop.init() { + IterableLoopInitializer::Identifier(ref ident) => { + self.context.create_mutable_binding(*ident, true); + let binding = self.context.set_mutable_binding(*ident); + let index = self.get_or_insert_binding(binding); + 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 { - 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); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; - } - }, - IterableLoopInitializer::Let(declaration) => match declaration { - Binding::Identifier(ident) => { - self.context.create_mutable_binding(*ident, false); - self.emit_binding(BindingOpcode::InitLet, *ident); - } - Binding::Pattern(pattern) => { - for ident in pattern.idents() { - self.context.create_mutable_binding(ident, false); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; - } - }, - 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::InitVar)?; + } + }, + IterableLoopInitializer::Let(declaration) => match declaration { + Binding::Identifier(ident) => { + self.context.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLet, *ident); + } + Binding::Pattern(pattern) => { + for ident in pattern.idents() { + self.context.create_mutable_binding(ident, false); + } + self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; + } + }, + 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_stmt(for_of_loop.body(), false)?; + self.compile_stmt(for_of_loop.body(), false)?; - 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); + 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); - 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.emit_opcode(Opcode::IteratorClose); - } - 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.patch_jump(exit); + self.pop_loop_control_info(); + self.emit_opcode(Opcode::LoopEnd); + self.emit_opcode(Opcode::IteratorClose); + Ok(()) + } - self.compile_expr(while_.condition(), true)?; - let exit = self.jump_if_false(); - self.compile_stmt(while_.body(), false)?; - self.emit(Opcode::Jump, &[start_address]); - self.patch_jump(exit); + #[inline] + pub fn compile_while_loop( + &mut self, + while_loop: &WhileLoop, + label: Option, + ) -> 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(); - self.emit_opcode(Opcode::LoopEnd); - } - Statement::DoWhileLoop(do_while) => { - self.emit_opcode(Opcode::LoopStart); - let initial_label = self.jump(); + #[inline] + pub fn compile_do_while_loop( + &mut self, + do_while_loop: &DoWhileLoop, + label: Option, + ) -> 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.emit_opcode(Opcode::LoopContinue); + let start_address = self.next_opcode_location(); + 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)?; - let exit = self.jump_if_false(); + let condition_label_address = self.next_opcode_location(); + self.compile_expr(do_while_loop.cond(), true)?; + let exit = self.jump_if_false(); - self.patch_jump(initial_label); + self.patch_jump(initial_label); - self.compile_stmt(do_while.body(), false)?; - self.emit(Opcode::Jump, &[condition_label_address]); - self.patch_jump(exit); + 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); + self.pop_loop_control_info(); + self.emit_opcode(Opcode::LoopEnd); + Ok(()) + } + + #[inline] + pub fn compile_block( + &mut self, + block: &Block, + label: Option, + 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, &[]); diff --git a/boa_engine/src/syntax/ast/declaration/mod.rs b/boa_engine/src/syntax/ast/declaration/mod.rs index 42413df8fe..77845cef42 100644 --- a/boa_engine/src/syntax/ast/declaration/mod.rs +++ b/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(';')), } } diff --git a/boa_engine/src/syntax/ast/statement/block.rs b/boa_engine/src/syntax/ast/statement/block.rs index 2f48cea544..12f2471ef0 100644 --- a/boa_engine/src/syntax/ast/statement/block.rs +++ b/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, } impl Block { @@ -49,20 +48,12 @@ impl Block { ) } - pub fn label(&self) -> Option { - 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, } } } diff --git a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs index a657e57081..47f7f33a6e 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs +++ b/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, condition: Expression, - label: Option, } impl DoWhileLoop { @@ -29,45 +28,26 @@ impl DoWhileLoop { pub fn cond(&self) -> &Expression { &self.condition } - - pub fn label(&self) -> Option { - 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] diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs index dfacdcc763..e74dbba07b 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs +++ b/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, - label: Option, } 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 { - 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] diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs index 760754f580..29dd2f49a3 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs +++ b/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, - label: Option, } 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 { - 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] diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs index 5fb4c31ca3..57838c6319 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs +++ b/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, - label: Option, 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 { - 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] diff --git a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs index 97699428c6..06c8515d75 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs +++ b/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, - label: Option, } impl WhileLoop { @@ -27,44 +26,25 @@ impl WhileLoop { pub fn body(&self) -> &Statement { &self.body } - - pub fn label(&self) -> Option { - 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] diff --git a/boa_engine/src/syntax/ast/statement/labelled.rs b/boa_engine/src/syntax/ast/statement/labelled.rs new file mode 100644 index 0000000000..1e250df0a9 --- /dev/null +++ b/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 for LabelledItem { + fn from(f: Function) -> Self { + Self::Function(f) + } +} + +impl From 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, + 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 for Statement { + fn from(labelled: Labelled) -> Self { + Self::Labelled(labelled) + } +} diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs index 4ba474c5d6..9edf371fb2 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/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) { @@ -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 { diff --git a/boa_engine/src/syntax/ast/statement/return.rs b/boa_engine/src/syntax/ast/statement/return.rs index 63596f4530..4254076c3b 100644 --- a/boa_engine/src/syntax/ast/statement/return.rs +++ b/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, - label: Option, } impl Return { - pub fn label(&self) -> Option { - self.label - } - pub fn expr(&self) -> Option<&Expression> { self.expr.as_ref() } /// Creates a `Return` AST node. - pub fn new(expr: Option, label: Option) -> Self { - Self { expr, label } + pub fn new(expr: Option) -> 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] diff --git a/boa_engine/src/syntax/ast/statement_list/mod.rs b/boa_engine/src/syntax/ast/statement_list/mod.rs index 70851603d5..0ba64d6f71 100644 --- a/boa_engine/src/syntax/ast/statement_list/mod.rs +++ b/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 diff --git a/boa_engine/src/syntax/parser/error.rs b/boa_engine/src/syntax/parser/error.rs index 1cf7d5f49d..2a42aa3e13 100644 --- a/boa_engine/src/syntax/parser/error.rs +++ b/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 } diff --git a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs index 79c663d246..145b8628bd 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/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()])), diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs index 3a40c64e0a..8d6f92df67 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs +++ b/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(), ) diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs index e36b6678c3..d66e1e7048 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs +++ b/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(), ) diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs index 4ff4306e52..5ccacc1bf8 100644 --- a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs +++ b/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(), ), diff --git a/boa_engine/src/syntax/parser/function/tests.rs b/boa_engine/src/syntax/parser/function/tests.rs index 64d1e3569b..c8563babd7 100644 --- a/boa_engine/src/syntax/parser/function/tests.rs +++ b/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( - 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, - ), + 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(), + )), ))] .into(), ))) @@ -379,17 +373,14 @@ fn check_arrow_semicolon_insertion() { length: 2, }, vec![StatementListItem::Statement(Statement::Return( - 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, - ), + 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(), + )), ))] .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(), ) diff --git a/boa_engine/src/syntax/parser/statement/block/tests.rs b/boa_engine/src/syntax/parser/statement/block/tests.rs index 727346cfa9..c097db178d 100644 --- a/boa_engine/src/syntax/parser/statement/block/tests.rs +++ b/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(), )) diff --git a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs index 64b20e9c7f..c8b4c7b922 100644 --- a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs +++ b/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,28 +124,31 @@ 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. - Block::from(vec![StatementListItem::Declaration( - Declaration::Function( - FunctionDeclaration::new( - self.allow_yield, - self.allow_await, - false, - ) - .parse(cursor, interner)?, - ), - )]) - .into(), - ) + // 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. + Block::from(vec![StatementListItem::Declaration( + Declaration::Function( + FunctionDeclaration::new( + self.allow_yield, + self.allow_await, + false, + ) + .parse(cursor, interner)?, + ), + )]) + .into() } - _ => Some( - Statement::new(self.allow_yield, self.allow_await, self.allow_return) - .parse(cursor, interner)?, - ), + _ => 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)) } } diff --git a/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs index 7e89a666ad..655a17b0f0 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs +++ b/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)) => { diff --git a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs index 1ad63bb8b5..86de3fd56e 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/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(); diff --git a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs index a2dd0d7aa4..eff204947b 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs +++ b/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)) } } diff --git a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs index 94b6580565..b6993b1c26 100644 --- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/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 TokenParser for LabelledStatement where R: Read, { - type Output = ast::Statement; + type Output = ast::statement::Labelled; fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { 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)) } } diff --git a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs b/boa_engine/src/syntax/parser/statement/return_stm/mod.rs index 47c0f4ffe4..ea94ff190f 100644 --- a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs +++ b/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))) } } diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests.rs index fa5597d83f..9433c7c7de 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/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(