From b4da172f91699adb69d1eccd868caa03a11cc5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Thu, 3 Nov 2022 09:44:58 +0000 Subject: [PATCH] Extract the ast to a crate (#2402) This should hopefully improve our compilation times, both from a clean build and from an incremental compilation snapshot. Next would be the parser, but it imports `Context`, so it'll require a bit more work. The number of file changes is obviously big, but almost nothing was changed, I just moved everything to another crate and readjusted the imports of the `parser` module. (Though, I did have to change some details, because there were some functions on the ast that returned `ParseError`s, and the tests had to be moved to the parser) --- Cargo.lock | 15 + Cargo.toml | 2 + boa_ast/Cargo.toml | 22 + .../ast => boa_ast/src}/declaration/mod.rs | 11 +- .../src}/declaration/variable.rs | 88 +- .../ast => boa_ast/src}/expression/access.rs | 24 +- .../ast => boa_ast/src}/expression/await.rs | 24 +- .../ast => boa_ast/src}/expression/call.rs | 30 +- .../src}/expression/identifier.rs | 40 +- boa_ast/src/expression/literal/array.rs | 236 ++++ .../src}/expression/literal/mod.rs | 4 +- boa_ast/src/expression/literal/object.rs | 338 +++++ .../src}/expression/literal/template.rs | 47 +- .../ast => boa_ast/src}/expression/mod.rs | 12 +- .../ast => boa_ast/src}/expression/new.rs | 26 +- boa_ast/src/expression/operator/assign/mod.rs | 207 +++ .../src}/expression/operator/assign/op.rs | 2 +- .../src}/expression/operator/binary/mod.rs | 12 +- .../src}/expression/operator/binary/op.rs | 10 +- .../src}/expression/operator/conditional.rs | 10 +- .../src}/expression/operator/mod.rs | 3 - .../src}/expression/operator/unary/mod.rs | 11 +- .../src}/expression/operator/unary/op.rs | 2 +- .../src}/expression/optional.rs | 16 +- .../ast => boa_ast/src}/expression/spread.rs | 61 +- .../src}/expression/tagged_template.rs | 46 +- .../ast => boa_ast/src}/expression/yield.rs | 8 +- .../src}/function/arrow_function.rs | 40 +- .../src}/function/async_function.rs | 35 +- .../src}/function/async_generator.rs | 16 +- .../ast => boa_ast/src}/function/class.rs | 178 +-- .../ast => boa_ast/src}/function/generator.rs | 14 +- .../ast => boa_ast/src}/function/mod.rs | 92 +- .../src}/function/parameters.rs | 226 ++-- .../src/syntax/ast => boa_ast/src}/keyword.rs | 10 +- .../syntax/ast/mod.rs => boa_ast/src/lib.rs | 111 +- .../src/syntax/ast => boa_ast/src}/pattern.rs | 67 +- .../syntax/ast => boa_ast/src}/position.rs | 17 +- .../syntax/ast => boa_ast/src}/property.rs | 33 +- .../syntax/ast => boa_ast/src}/punctuator.rs | 7 +- .../ast => boa_ast/src}/statement/block.rs | 40 +- .../ast => boa_ast/src}/statement/if.rs | 26 +- .../src}/statement/iteration/break.rs | 42 +- .../src}/statement/iteration/continue.rs | 8 +- .../src}/statement/iteration/do_while_loop.rs | 9 +- .../src}/statement/iteration/for_in_loop.rs | 12 +- .../src}/statement/iteration/for_loop.rs | 24 +- .../src}/statement/iteration/for_of_loop.rs | 15 +- .../src}/statement/iteration/mod.rs | 12 +- .../src}/statement/iteration/while_loop.rs | 9 +- .../ast => boa_ast/src}/statement/labelled.rs | 11 +- .../ast => boa_ast/src}/statement/mod.rs | 27 +- .../ast => boa_ast/src}/statement/return.rs | 28 +- .../mod.rs => boa_ast/src/statement/switch.rs | 18 +- .../ast => boa_ast/src}/statement/throw.rs | 24 +- .../mod.rs => boa_ast/src/statement/try.rs | 97 +- .../mod.rs => boa_ast/src/statement_list.rs | 41 +- .../src/syntax/ast => boa_ast/src}/visitor.rs | 26 +- boa_cli/Cargo.toml | 1 + boa_cli/src/main.rs | 3 +- boa_engine/Cargo.toml | 3 +- boa_engine/src/builtins/function/arguments.rs | 4 +- boa_engine/src/builtins/function/mod.rs | 13 +- boa_engine/src/bytecompiler/function.rs | 4 +- boa_engine/src/bytecompiler/mod.rs | 62 +- boa_engine/src/context/mod.rs | 3 +- boa_engine/src/environments/compile.rs | 4 +- boa_engine/src/environments/runtime.rs | 4 +- .../syntax/ast/expression/literal/array.rs | 144 -- .../ast/expression/literal/object/mod.rs | 180 --- .../ast/expression/literal/object/tests.rs | 109 -- .../ast/expression/operator/assign/mod.rs | 475 ------- .../syntax/ast/expression/operator/tests.rs | 140 -- .../syntax/ast/statement/iteration/tests.rs | 558 -------- .../src/syntax/ast/statement/switch/tests.rs | 243 ---- .../src/syntax/ast/statement/try/tests.rs | 147 -- .../src/syntax/ast/statement_list/tests.rs | 118 -- boa_engine/src/syntax/lexer/comment.rs | 6 +- boa_engine/src/syntax/lexer/cursor.rs | 2 +- boa_engine/src/syntax/lexer/error.rs | 2 +- boa_engine/src/syntax/lexer/identifier.rs | 6 +- boa_engine/src/syntax/lexer/mod.rs | 2 +- boa_engine/src/syntax/lexer/number.rs | 6 +- boa_engine/src/syntax/lexer/operator.rs | 6 +- .../src/syntax/lexer/private_identifier.rs | 6 +- boa_engine/src/syntax/lexer/regex.rs | 6 +- boa_engine/src/syntax/lexer/spread.rs | 6 +- boa_engine/src/syntax/lexer/string.rs | 6 +- boa_engine/src/syntax/lexer/template.rs | 15 +- boa_engine/src/syntax/lexer/tests.rs | 10 +- boa_engine/src/syntax/lexer/token.rs | 14 +- boa_engine/src/syntax/mod.rs | 3 +- .../parser/cursor/buffered_lexer/mod.rs | 2 +- boa_engine/src/syntax/parser/cursor/mod.rs | 6 +- boa_engine/src/syntax/parser/error.rs | 7 +- .../expression/assignment/arrow_function.rs | 84 +- .../expression/assignment/conditional.rs | 8 +- .../expression/assignment/exponentiation.rs | 14 +- .../parser/expression/assignment/mod.rs | 28 +- .../parser/expression/assignment/yield.rs | 2 +- .../syntax/parser/expression/await_expr.rs | 2 +- .../syntax/parser/expression/identifiers.rs | 2 +- .../expression/left_hand_side/arguments.rs | 2 +- .../parser/expression/left_hand_side/call.rs | 16 +- .../expression/left_hand_side/member.rs | 21 +- .../parser/expression/left_hand_side/mod.rs | 8 +- .../expression/left_hand_side/optional/mod.rs | 12 +- .../left_hand_side/optional/tests.rs | 14 +- .../expression/left_hand_side/template.rs | 2 +- .../parser/expression/left_hand_side/tests.rs | 10 +- .../src/syntax/parser/expression/mod.rs | 125 +- .../primary/array_initializer/mod.rs | 8 +- .../primary/array_initializer/tests.rs | 10 +- .../primary/async_function_expression/mod.rs | 11 +- .../async_function_expression/tests.rs | 16 +- .../primary/async_generator_expression/mod.rs | 11 +- .../async_generator_expression/tests.rs | 16 +- .../primary/class_expression/mod.rs | 2 +- .../primary/function_expression/mod.rs | 12 +- .../primary/function_expression/tests.rs | 16 +- .../primary/generator_expression/mod.rs | 12 +- .../primary/generator_expression/tests.rs | 14 +- .../syntax/parser/expression/primary/mod.rs | 70 +- .../primary/object_initializer/mod.rs | 33 +- .../primary/object_initializer/tests.rs | 80 +- .../parser/expression/primary/template/mod.rs | 8 +- .../syntax/parser/expression/primary/tests.rs | 6 +- .../src/syntax/parser/expression/tests.rs | 24 +- .../src/syntax/parser/expression/unary.rs | 16 +- .../src/syntax/parser/expression/update.rs | 20 +- boa_engine/src/syntax/parser/function/mod.rs | 70 +- .../src/syntax/parser/function/tests.rs | 600 ++++----- boa_engine/src/syntax/parser/mod.rs | 60 +- .../src/syntax/parser/statement/block/mod.rs | 2 +- .../syntax/parser/statement/block/tests.rs | 22 +- .../syntax/parser/statement/break_stm/mod.rs | 2 +- .../parser/statement/break_stm/tests.rs | 12 +- .../parser/statement/continue_stm/mod.rs | 2 +- .../parser/statement/continue_stm/tests.rs | 12 +- .../hoistable/async_function_decl/mod.rs | 11 +- .../hoistable/async_function_decl/tests.rs | 10 +- .../hoistable/async_generator_decl/mod.rs | 10 +- .../hoistable/async_generator_decl/tests.rs | 10 +- .../declaration/hoistable/class_decl/mod.rs | 19 +- .../declaration/hoistable/class_decl/tests.rs | 14 +- .../hoistable/function_decl/mod.rs | 10 +- .../hoistable/function_decl/tests.rs | 10 +- .../hoistable/generator_decl/mod.rs | 11 +- .../hoistable/generator_decl/tests.rs | 10 +- .../statement/declaration/hoistable/mod.rs | 13 +- .../parser/statement/declaration/lexical.rs | 2 +- .../parser/statement/declaration/mod.rs | 2 +- .../parser/statement/declaration/tests.rs | 12 +- .../syntax/parser/statement/expression/mod.rs | 2 +- .../src/syntax/parser/statement/if_stm/mod.rs | 8 +- .../syntax/parser/statement/if_stm/tests.rs | 12 +- .../statement/iteration/do_while_statement.rs | 2 +- .../statement/iteration/for_statement.rs | 53 +- .../parser/statement/iteration/tests.rs | 24 +- .../statement/iteration/while_statement.rs | 10 +- .../parser/statement/labelled_stm/mod.rs | 2 +- boa_engine/src/syntax/parser/statement/mod.rs | 10 +- .../syntax/parser/statement/return_stm/mod.rs | 2 +- .../src/syntax/parser/statement/switch/mod.rs | 4 +- .../syntax/parser/statement/switch/tests.rs | 14 +- .../src/syntax/parser/statement/throw/mod.rs | 2 +- .../syntax/parser/statement/throw/tests.rs | 6 +- .../syntax/parser/statement/try_stm/catch.rs | 2 +- .../parser/statement/try_stm/finally.rs | 9 +- .../syntax/parser/statement/try_stm/mod.rs | 16 +- .../syntax/parser/statement/try_stm/tests.rs | 57 +- .../syntax/parser/statement/variable/mod.rs | 18 +- .../syntax/parser/tests/format/declaration.rs | 42 + .../syntax/parser/tests/format/expression.rs | 137 ++ .../parser/tests/format/function/class.rs | 106 ++ .../parser/tests/format/function/mod.rs | 48 + .../src/syntax/parser/tests/format/mod.rs | 47 + .../syntax/parser/tests/format/statement.rs | 124 ++ .../syntax/parser/{tests.rs => tests/mod.rs} | 58 +- boa_engine/src/tests.rs | 1191 +++++++++++++++++ boa_engine/src/vm/code_block.rs | 34 +- boa_examples/Cargo.toml | 1 + boa_examples/src/bin/commuter_visitor.rs | 42 +- boa_examples/src/bin/symbol_visitor.rs | 10 +- 184 files changed, 4254 insertions(+), 4448 deletions(-) create mode 100644 boa_ast/Cargo.toml rename {boa_engine/src/syntax/ast => boa_ast/src}/declaration/mod.rs (96%) rename {boa_engine/src/syntax/ast => boa_ast/src}/declaration/variable.rs (88%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/access.rs (94%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/await.rs (76%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/call.rs (89%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/identifier.rs (76%) create mode 100644 boa_ast/src/expression/literal/array.rs rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/literal/mod.rs (98%) create mode 100644 boa_ast/src/expression/literal/object.rs rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/literal/template.rs (79%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/mod.rs (97%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/new.rs (82%) create mode 100644 boa_ast/src/expression/operator/assign/mod.rs rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/assign/op.rs (99%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/binary/mod.rs (91%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/binary/op.rs (98%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/conditional.rs (93%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/mod.rs (96%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/unary/mod.rs (90%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/operator/unary/op.rs (99%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/optional.rs (95%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/spread.rs (66%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/tagged_template.rs (79%) rename {boa_engine/src/syntax/ast => boa_ast/src}/expression/yield.rs (93%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/arrow_function.rs (81%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/async_function.rs (82%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/async_generator.rs (92%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/class.rs (83%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/generator.rs (92%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/mod.rs (65%) rename {boa_engine/src/syntax/ast => boa_ast/src}/function/parameters.rs (72%) rename {boa_engine/src/syntax/ast => boa_ast/src}/keyword.rs (99%) rename boa_engine/src/syntax/ast/mod.rs => boa_ast/src/lib.rs (50%) rename {boa_engine/src/syntax/ast => boa_ast/src}/pattern.rs (97%) rename {boa_engine/src/syntax/ast => boa_ast/src}/position.rs (95%) rename {boa_engine/src/syntax/ast => boa_ast/src}/property.rs (95%) rename {boa_engine/src/syntax/ast => boa_ast/src}/punctuator.rs (98%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/block.rs (71%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/if.rs (89%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/break.rs (71%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/continue.rs (92%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/do_while_loop.rs (91%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/for_in_loop.rs (94%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/for_loop.rs (95%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/for_of_loop.rs (93%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/mod.rs (94%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/iteration/while_loop.rs (91%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/labelled.rs (94%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/mod.rs (95%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/return.rs (80%) rename boa_engine/src/syntax/ast/statement/switch/mod.rs => boa_ast/src/statement/switch.rs (94%) rename {boa_engine/src/syntax/ast => boa_ast/src}/statement/throw.rs (81%) rename boa_engine/src/syntax/ast/statement/try/mod.rs => boa_ast/src/statement/try.rs (74%) rename boa_engine/src/syntax/ast/statement_list/mod.rs => boa_ast/src/statement_list.rs (88%) rename {boa_engine/src/syntax/ast => boa_ast/src}/visitor.rs (94%) delete mode 100644 boa_engine/src/syntax/ast/expression/literal/array.rs delete mode 100644 boa_engine/src/syntax/ast/expression/literal/object/mod.rs delete mode 100644 boa_engine/src/syntax/ast/expression/literal/object/tests.rs delete mode 100644 boa_engine/src/syntax/ast/expression/operator/assign/mod.rs delete mode 100644 boa_engine/src/syntax/ast/expression/operator/tests.rs delete mode 100644 boa_engine/src/syntax/ast/statement/iteration/tests.rs delete mode 100644 boa_engine/src/syntax/ast/statement/switch/tests.rs delete mode 100644 boa_engine/src/syntax/ast/statement/try/tests.rs delete mode 100644 boa_engine/src/syntax/ast/statement_list/tests.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/declaration.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/expression.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/function/class.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/function/mod.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/mod.rs create mode 100644 boa_engine/src/syntax/parser/tests/format/statement.rs rename boa_engine/src/syntax/parser/{tests.rs => tests/mod.rs} (92%) diff --git a/Cargo.lock b/Cargo.lock index 00c71dbf3c..7e98a81c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,10 +55,23 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "boa_ast" +version = "0.16.0" +dependencies = [ + "bitflags", + "boa_interner", + "boa_macros", + "num-bigint", + "rustc-hash", + "serde", +] + [[package]] name = "boa_cli" version = "0.16.0" dependencies = [ + "boa_ast", "boa_engine", "boa_interner", "clap 4.0.18", @@ -76,6 +89,7 @@ name = "boa_engine" version = "0.16.0" dependencies = [ "bitflags", + "boa_ast", "boa_gc", "boa_interner", "boa_macros", @@ -117,6 +131,7 @@ dependencies = [ name = "boa_examples" version = "0.16.0" dependencies = [ + "boa_ast", "boa_engine", "boa_gc", "boa_interner", diff --git a/Cargo.toml b/Cargo.toml index cffa4bbe1e..bfd347d784 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "boa_cli", "boa_engine", + "boa_ast", "boa_gc", "boa_interner", "boa_profiler", @@ -28,6 +29,7 @@ boa_gc = { version = "0.16.0", path = "boa_gc" } boa_profiler = { version = "0.16.0", path = "boa_profiler" } boa_unicode = { version = "0.16.0", path = "boa_unicode" } boa_macros = { version = "0.16.0", path = "boa_macros" } +boa_ast = { version = "0.16.0", path = "boa_ast" } [workspace.metadata.workspaces] allow_branch = "main" diff --git a/boa_ast/Cargo.toml b/boa_ast/Cargo.toml new file mode 100644 index 0000000000..b248436cf1 --- /dev/null +++ b/boa_ast/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "boa_ast" +description = "Abstract Syntax Tree definition for the Boa JavaScript engine." +keywords = ["javascript", "js", "syntax", "ast"] +categories = ["parser-implementations", "compilers"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[features] +serde = ["boa_interner/serde", "dep:serde"] + +[dependencies] +boa_interner.workspace = true +boa_macros.workspace = true +rustc-hash = "1.1.0" +serde = { version = "1.0.147", features = ["derive"], optional = true } +bitflags = "1.3.2" +num-bigint = "0.4.3" diff --git a/boa_engine/src/syntax/ast/declaration/mod.rs b/boa_ast/src/declaration/mod.rs similarity index 96% rename from boa_engine/src/syntax/ast/declaration/mod.rs rename to boa_ast/src/declaration/mod.rs index eca685b815..a1c03cd1d7 100644 --- a/boa_engine/src/syntax/ast/declaration/mod.rs +++ b/boa_ast/src/declaration/mod.rs @@ -21,17 +21,16 @@ use super::{ }; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; -use tap::Tap; mod variable; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; pub use variable::*; /// The `Declaration` Parse Node. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Declaration { /// See [`Function`] @@ -163,7 +162,11 @@ impl ToIndentedString for 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).tap_mut(|s| s.push(';')), + Declaration::Lexical(l) => { + let mut s = l.to_interned_string(interner); + s.push(';'); + s + } } } } diff --git a/boa_engine/src/syntax/ast/declaration/variable.rs b/boa_ast/src/declaration/variable.rs similarity index 88% rename from boa_engine/src/syntax/ast/declaration/variable.rs rename to boa_ast/src/declaration/variable.rs index c2ea06b86e..2a2536bf4d 100644 --- a/boa_engine/src/syntax/ast/declaration/variable.rs +++ b/boa_ast/src/declaration/variable.rs @@ -3,14 +3,14 @@ use core::ops::ControlFlow; use std::convert::TryFrom; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ expression::{Expression, Identifier}, join_nodes, pattern::Pattern, ContainsSymbol, Statement, }; -use crate::try_break; use boa_interner::{Interner, ToInternedString}; use super::Declaration; @@ -44,7 +44,7 @@ use super::Declaration; /// [var]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var /// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement /// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct VarDeclaration(pub VariableList); @@ -91,7 +91,7 @@ impl VisitWith for VarDeclaration { /// the variable declaration. /// /// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum LexicalDeclaration { /// A [const] variable creates a constant whose scope can be either global or local @@ -118,6 +118,7 @@ pub enum LexicalDeclaration { impl LexicalDeclaration { /// Gets the inner variable list of the `LexicalDeclaration` + #[must_use] pub fn variable_list(&self) -> &VariableList { match self { LexicalDeclaration::Const(list) | LexicalDeclaration::Let(list) => list, @@ -185,7 +186,7 @@ impl VisitWith for LexicalDeclaration { } /// List of variables in a variable declaration. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct VariableList { list: Box<[Variable]>, @@ -193,6 +194,7 @@ pub struct VariableList { impl VariableList { /// Creates a variable list if the provided list of [`Variable`] is not empty. + #[must_use] pub fn new(list: Box<[Variable]>) -> Option { if list.is_empty() { return None; @@ -274,7 +276,7 @@ impl TryFrom> for VariableList { /// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding /// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration /// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Variable { binding: Binding, @@ -295,7 +297,8 @@ impl ToInternedString for Variable { impl Variable { /// Creates a new variable declaration from a `BindingIdentifier`. #[inline] - pub(in crate::syntax) fn from_identifier(ident: Identifier, init: Option) -> Self { + #[must_use] + pub fn from_identifier(ident: Identifier, init: Option) -> Self { Self { binding: Binding::Identifier(ident), init, @@ -304,23 +307,32 @@ impl Variable { /// Creates a new variable declaration from a `Pattern`. #[inline] - pub(in crate::syntax) fn from_pattern(pattern: Pattern, init: Option) -> Self { + #[must_use] + pub fn from_pattern(pattern: Pattern, init: Option) -> Self { Self { binding: Binding::Pattern(pattern), init, } } /// Gets the variable declaration binding. - pub(crate) fn binding(&self) -> &Binding { + #[must_use] + pub fn binding(&self) -> &Binding { &self.binding } /// Gets the initialization expression for the variable declaration, if any. #[inline] - pub(crate) fn init(&self) -> Option<&Expression> { + #[must_use] + pub fn init(&self) -> Option<&Expression> { self.init.as_ref() } + /// Gets the list of declared identifiers. + #[must_use] + pub fn idents(&self) -> Vec { + self.binding.idents() + } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { if let Some(ref node) = self.init { @@ -338,7 +350,8 @@ impl Variable { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + #[must_use] + pub fn contains(&self, symbol: ContainsSymbol) -> bool { if let Some(ref node) = self.init { if node.contains(symbol) { return true; @@ -346,11 +359,6 @@ impl Variable { } self.binding.contains(symbol) } - - /// Gets the list of declared identifiers. - pub(crate) fn idents(&self) -> Vec { - self.binding.idents() - } } impl VisitWith for Variable { @@ -383,7 +391,7 @@ impl VisitWith for Variable { /// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Binding { /// A single identifier binding. @@ -458,47 +466,3 @@ impl VisitWith for Binding { } } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt_binding_pattern() { - crate::syntax::ast::test_formatting( - r#" - var { } = { - o: "1", - }; - var { o_v1 } = { - o_v1: "1", - }; - var { o_v2 = "1" } = { - o_v2: "2", - }; - var { a : o_v3 = "1" } = { - a: "2", - }; - var { ... o_rest_v1 } = { - a: "2", - }; - var { o_v4, o_v5, o_v6 = "1", a : o_v7 = "1", ... o_rest_v2 } = { - o_v4: "1", - o_v5: "1", - }; - var [] = []; - var [ , ] = []; - var [ a_v1 ] = [1, 2, 3]; - var [ a_v2, a_v3 ] = [1, 2, 3]; - var [ a_v2, , a_v3 ] = [1, 2, 3]; - var [ ... a_rest_v1 ] = [1, 2, 3]; - var [ a_v4, , ... a_rest_v2 ] = [1, 2, 3]; - var [ { a_v5 } ] = [{ - a_v5: 1, - }, { - a_v5: 2, - }, { - a_v5: 3, - }]; - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_ast/src/expression/access.rs similarity index 94% rename from boa_engine/src/syntax/ast/expression/access.rs rename to boa_ast/src/expression/access.rs index 2c4e2b3257..520d20c377 100644 --- a/boa_engine/src/syntax/ast/expression/access.rs +++ b/boa_ast/src/expression/access.rs @@ -14,16 +14,16 @@ //! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors //! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, ContainsSymbol}; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; /// A property access field. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum PropertyAccessField { /// A constant property field, such as `x.prop`. @@ -87,7 +87,7 @@ impl VisitWith for PropertyAccessField { /// A property access expression. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum PropertyAccess { /// A simple property access (`x.prop`). @@ -161,7 +161,7 @@ impl VisitWith for PropertyAccess { } /// A simple property access, where the target object is an [`Expression`]. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct SimplePropertyAccess { target: Box, @@ -171,12 +171,14 @@ pub struct SimplePropertyAccess { impl SimplePropertyAccess { /// Gets the target object of the property access. #[inline] + #[must_use] pub fn target(&self) -> &Expression { &self.target } /// Gets the accessed field of the target object. #[inline] + #[must_use] pub fn field(&self) -> &PropertyAccessField { &self.field } @@ -252,7 +254,7 @@ impl VisitWith for SimplePropertyAccess { /// /// [spec]: https://tc39.es/ecma262/#prod-MemberExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct PrivatePropertyAccess { target: Box, @@ -262,6 +264,7 @@ pub struct PrivatePropertyAccess { impl PrivatePropertyAccess { /// Creates a `GetPrivateField` AST Expression. #[inline] + #[must_use] pub fn new(value: Expression, field: Sym) -> Self { Self { target: value.into(), @@ -271,12 +274,14 @@ impl PrivatePropertyAccess { /// Gets the original object from where to get the field from. #[inline] + #[must_use] pub fn target(&self) -> &Expression { &self.target } /// Gets the name of the field to retrieve. #[inline] + #[must_use] pub fn field(&self) -> Sym { self.field } @@ -335,19 +340,22 @@ impl VisitWith for PrivatePropertyAccess { /// /// [spec]: https://tc39.es/ecma262/#prod-SuperProperty /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct SuperPropertyAccess { field: PropertyAccessField, } impl SuperPropertyAccess { - pub(in crate::syntax) fn new(field: PropertyAccessField) -> Self { + /// Creates a new property access field node. + #[must_use] + pub fn new(field: PropertyAccessField) -> Self { Self { field } } /// Gets the name of the field to retrieve. #[inline] + #[must_use] pub fn field(&self) -> &PropertyAccessField { &self.field } diff --git a/boa_engine/src/syntax/ast/expression/await.rs b/boa_ast/src/expression/await.rs similarity index 76% rename from boa_engine/src/syntax/ast/expression/await.rs rename to boa_ast/src/expression/await.rs index 00154c3b78..604f99f6c9 100644 --- a/boa_engine/src/syntax/ast/expression/await.rs +++ b/boa_ast/src/expression/await.rs @@ -1,10 +1,10 @@ //! Await expression Expression. -use crate::syntax::ast::ContainsSymbol; +use crate::ContainsSymbol; use core::ops::ControlFlow; use super::Expression; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; /// An await expression is used within an async function to pause execution and wait for a @@ -16,7 +16,7 @@ use boa_interner::{Interner, ToIndentedString, ToInternedString}; /// /// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Await { target: Box, @@ -25,7 +25,8 @@ pub struct Await { impl Await { /// Return the target expression that should be awaited. #[inline] - pub(crate) fn target(&self) -> &Expression { + #[must_use] + pub fn target(&self) -> &Expression { &self.target } @@ -79,18 +80,3 @@ impl VisitWith for Await { visitor.visit_expression_mut(&mut self.target) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented. - crate::syntax::ast::test_formatting( - r#" - async function f() { - await function_call(); - } - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/call.rs b/boa_ast/src/expression/call.rs similarity index 89% rename from boa_engine/src/syntax/ast/expression/call.rs rename to boa_ast/src/expression/call.rs index f99bc9956b..2262663155 100644 --- a/boa_engine/src/syntax/ast/expression/call.rs +++ b/boa_ast/src/expression/call.rs @@ -1,6 +1,6 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{join_nodes, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{join_nodes, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; @@ -20,7 +20,7 @@ use super::Expression; /// /// [spec]: https://tc39.es/ecma262/#prod-CallExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Call { function: Box, @@ -30,6 +30,7 @@ pub struct Call { impl Call { /// Creates a new `Call` AST Expression. #[inline] + #[must_use] pub fn new(function: Expression, args: Box<[Expression]>) -> Self { Self { function: function.into(), @@ -39,12 +40,14 @@ impl Call { /// Gets the target function of this call expression. #[inline] + #[must_use] pub fn function(&self) -> &Expression { &self.function } /// Retrieves the arguments passed to the function. #[inline] + #[must_use] pub fn args(&self) -> &[Expression] { &self.args } @@ -110,7 +113,7 @@ impl VisitWith for Call { /// /// [spec]: https://tc39.es/ecma262/#prod-SuperCall /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct SuperCall { args: Box<[Expression]>, @@ -118,7 +121,7 @@ pub struct SuperCall { impl SuperCall { /// Creates a new `SuperCall` AST node. - pub(crate) fn new(args: A) -> Self + pub fn new(args: A) -> Self where A: Into>, { @@ -126,7 +129,8 @@ impl SuperCall { } /// Retrieves the arguments of the super call. - pub(crate) fn args(&self) -> &[Expression] { + #[must_use] + pub fn arguments(&self) -> &[Expression] { &self.args } @@ -176,17 +180,3 @@ impl VisitWith for SuperCall { ControlFlow::Continue(()) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - call_1(1, 2, 3); - call_2("argument here"); - call_3(); - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/identifier.rs b/boa_ast/src/expression/identifier.rs similarity index 76% rename from boa_engine/src/syntax/ast/expression/identifier.rs rename to boa_ast/src/expression/identifier.rs index c9b753bed3..11320d06bc 100644 --- a/boa_engine/src/syntax/ast/expression/identifier.rs +++ b/boa_ast/src/expression/identifier.rs @@ -1,15 +1,27 @@ //! Local identifier Expression. -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::{ - string::ToStringEscaped, - syntax::{ast::Position, parser::ParseError}, + visitor::{VisitWith, Visitor, VisitorMut}, + ToStringEscaped, }; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; use super::Expression; +/// List of reserved keywords exclusive to strict mode. +pub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [ + Sym::IMPLEMENTS, + Sym::INTERFACE, + Sym::LET, + Sym::PACKAGE, + Sym::PRIVATE, + Sym::PROTECTED, + Sym::PUBLIC, + Sym::STATIC, + Sym::YIELD, +]; + /// An `identifier` is a sequence of characters in the code that identifies a variable, /// function, or property. /// @@ -27,7 +39,7 @@ use super::Expression; /// [spec]: https://tc39.es/ecma262/#prod-Identifier /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier #[cfg_attr( - feature = "deser", + feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent) )] @@ -54,33 +66,17 @@ impl PartialEq for Sym { impl Identifier { /// Creates a new identifier AST Expression. #[inline] + #[must_use] pub fn new(ident: Sym) -> Self { Self { ident } } /// Retrieves the identifier's string symbol in the interner. #[inline] + #[must_use] pub fn sym(self) -> Sym { self.ident } - - /// Returns an error if `arguments` or `eval` are used as identifier in strict mode. - pub(crate) fn check_strict_arguments_or_eval( - self, - position: Position, - ) -> Result<(), ParseError> { - match self.ident { - Sym::ARGUMENTS => Err(ParseError::general( - "unexpected identifier 'arguments' in strict mode", - position, - )), - Sym::EVAL => Err(ParseError::general( - "unexpected identifier 'eval' in strict mode", - position, - )), - _ => Ok(()), - } - } } impl ToInternedString for Identifier { diff --git a/boa_ast/src/expression/literal/array.rs b/boa_ast/src/expression/literal/array.rs new file mode 100644 index 0000000000..8fc0875f75 --- /dev/null +++ b/boa_ast/src/expression/literal/array.rs @@ -0,0 +1,236 @@ +//! Array declaration Expression. + +use crate::expression::operator::assign::AssignTarget; +use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern}; +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, ContainsSymbol}; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; + +/// An array is an ordered collection of data (either primitive or object depending upon the +/// language). +/// +/// Arrays are used to store multiple values in a single variable. +/// This is compared to a variable that can store only one value. +/// +/// Each item in an array has a number attached to it, called a numeric index, that allows you +/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various +/// methods. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct ArrayLiteral { + arr: Box<[Option]>, + has_trailing_comma_spread: bool, +} + +impl ArrayLiteral { + /// Creates a new array literal. + pub fn new(array: A, has_trailing_comma_spread: bool) -> Self + where + A: Into]>>, + { + Self { + arr: array.into(), + has_trailing_comma_spread, + } + } + + /// Indicates if a spread operator in the array literal has a trailing comma. + /// This is a syntax error in some cases. + #[must_use] + pub fn has_trailing_comma_spread(&self) -> bool { + self.has_trailing_comma_spread + } + + /// Converts this `ArrayLiteral` into an [`ArrayPattern`]. + #[must_use] + pub fn to_pattern(&self, strict: bool) -> Option { + if self.has_trailing_comma_spread() { + return None; + } + + let mut bindings = Vec::new(); + for (i, expr) in self.arr.iter().enumerate() { + let expr = if let Some(expr) = expr { + expr + } else { + bindings.push(ArrayPatternElement::Elision); + continue; + }; + match expr { + Expression::Identifier(ident) => { + if strict && *ident == Sym::ARGUMENTS { + return None; + } + + bindings.push(ArrayPatternElement::SingleName { + ident: *ident, + default_init: None, + }); + } + Expression::Spread(spread) => { + match spread.target() { + Expression::Identifier(ident) => { + bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident }); + } + Expression::PropertyAccess(access) => { + bindings.push(ArrayPatternElement::PropertyAccessRest { + access: access.clone(), + }); + } + Expression::ArrayLiteral(array) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::PatternRest { pattern }); + } + Expression::ObjectLiteral(object) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::PatternRest { pattern }); + } + _ => return None, + } + if i + 1 != self.arr.len() { + return None; + } + } + Expression::Assign(assign) => match assign.lhs() { + AssignTarget::Identifier(ident) => { + bindings.push(ArrayPatternElement::SingleName { + ident: *ident, + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Access(access) => { + bindings.push(ArrayPatternElement::PropertyAccess { + access: access.clone(), + }); + } + AssignTarget::Pattern(pattern) => match pattern { + Pattern::Object(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Object(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + Pattern::Array(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Array(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + }, + }, + Expression::ArrayLiteral(array) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::ObjectLiteral(object) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ArrayPatternElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::PropertyAccess(access) => { + bindings.push(ArrayPatternElement::PropertyAccess { + access: access.clone(), + }); + } + _ => return None, + } + } + Some(ArrayPattern::new(bindings.into())) + } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.arr + .iter() + .flatten() + .any(Expression::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.arr.iter().flatten().any(|expr| expr.contains(symbol)) + } +} + +impl AsRef<[Option]> for ArrayLiteral { + #[inline] + fn as_ref(&self) -> &[Option] { + &self.arr + } +} + +impl From for ArrayLiteral +where + T: Into]>>, +{ + #[inline] + fn from(decl: T) -> Self { + Self { + arr: decl.into(), + has_trailing_comma_spread: false, + } + } +} + +impl ToInternedString for ArrayLiteral { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = String::from("["); + let mut first = true; + for e in &*self.arr { + if first { + first = false; + } else { + buf.push_str(", "); + } + if let Some(e) = e { + buf.push_str(&e.to_interned_string(interner)); + } + } + buf.push(']'); + buf + } +} + +impl From for Expression { + #[inline] + fn from(arr: ArrayLiteral) -> Self { + Self::ArrayLiteral(arr) + } +} + +impl VisitWith for ArrayLiteral { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for expr in self.arr.iter().flatten() { + try_break!(visitor.visit_expression(expr)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for expr in self.arr.iter_mut().flatten() { + try_break!(visitor.visit_expression_mut(expr)); + } + ControlFlow::Continue(()) + } +} diff --git a/boa_engine/src/syntax/ast/expression/literal/mod.rs b/boa_ast/src/expression/literal/mod.rs similarity index 98% rename from boa_engine/src/syntax/ast/expression/literal/mod.rs rename to boa_ast/src/expression/literal/mod.rs index dac95a4e7f..bc8692d7fb 100644 --- a/boa_engine/src/syntax/ast/expression/literal/mod.rs +++ b/boa_ast/src/expression/literal/mod.rs @@ -16,7 +16,7 @@ use core::ops::ControlFlow; pub use object::ObjectLiteral; pub use template::{TemplateElement, TemplateLiteral}; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; use num_bigint::BigInt; @@ -32,7 +32,7 @@ use super::Expression; /// /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Literal { /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. diff --git a/boa_ast/src/expression/literal/object.rs b/boa_ast/src/expression/literal/object.rs new file mode 100644 index 0000000000..1458dfdc33 --- /dev/null +++ b/boa_ast/src/expression/literal/object.rs @@ -0,0 +1,338 @@ +//! Object Expression. + +use crate::{ + block_to_string, + expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT}, + join_nodes, + pattern::{ObjectPattern, ObjectPatternElement}, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + ContainsSymbol, +}; +use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; +use core::ops::ControlFlow; + +/// Objects in JavaScript may be defined as an unordered collection of related data, of +/// primitive or reference types, in the form of “key: value” pairs. +/// +/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal +/// notation. +/// +/// An object initializer is an expression that describes the initialization of an +/// [`Object`][object]. Objects consist of properties, which are used to describe an object. +/// Values of object properties can either contain [`primitive`][primitive] data types or other +/// objects. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer +/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object +/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +#[derive(Clone, Debug, PartialEq)] +pub struct ObjectLiteral { + properties: Box<[PropertyDefinition]>, +} + +impl ObjectLiteral { + /// Gets the object literal properties + #[inline] + #[must_use] + pub fn properties(&self) -> &[PropertyDefinition] { + &self.properties + } + + /// Converts the object literal into an [`ObjectPattern`]. + #[must_use] + pub fn to_pattern(&self, strict: bool) -> Option { + let mut bindings = Vec::new(); + let mut excluded_keys = Vec::new(); + for (i, property) in self.properties.iter().enumerate() { + match property { + PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { + return None + } + PropertyDefinition::IdentifierReference(ident) => { + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) { + return None; + } + + excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(ident.sym()), + default_init: None, + }); + } + PropertyDefinition::Property(name, expr) => match (name, expr) { + (PropertyName::Literal(name), Expression::Identifier(ident)) + if *name == *ident => + { + if strict && *name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) { + return None; + } + + excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*name), + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::Identifier(ident)) => { + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*name), + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => { + let pattern = object.to_pattern(strict)?.into(); + bindings.push(ObjectPatternElement::Pattern { + name: PropertyName::Literal(*name), + pattern, + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => { + let pattern = array.to_pattern(strict)?.into(); + bindings.push(ObjectPatternElement::Pattern { + name: PropertyName::Literal(*name), + pattern, + default_init: None, + }); + } + (_, Expression::Assign(assign)) => match assign.lhs() { + AssignTarget::Identifier(ident) => { + if let Some(name) = name.literal() { + if name == *ident { + if strict && name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { + return None; + } + excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } else { + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } + } else { + return None; + } + } + AssignTarget::Pattern(pattern) => { + bindings.push(ObjectPatternElement::Pattern { + name: name.clone(), + pattern: pattern.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Access(access) => { + bindings.push(ObjectPatternElement::AssignmentPropertyAccess { + name: name.clone(), + access: access.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + }, + (_, Expression::PropertyAccess(access)) => { + bindings.push(ObjectPatternElement::AssignmentPropertyAccess { + name: name.clone(), + access: access.clone(), + default_init: None, + }); + } + (PropertyName::Computed(name), Expression::Identifier(ident)) => { + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Computed(name.clone()), + default_init: None, + }); + } + _ => return None, + }, + PropertyDefinition::SpreadObject(spread) => { + match spread { + Expression::Identifier(ident) => { + bindings.push(ObjectPatternElement::RestProperty { + ident: *ident, + excluded_keys: excluded_keys.clone(), + }); + } + Expression::PropertyAccess(access) => { + bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess { + access: access.clone(), + excluded_keys: excluded_keys.clone(), + }); + } + _ => return None, + } + if i + 1 != self.properties.len() { + return None; + } + } + PropertyDefinition::MethodDefinition(_, _) => return None, + PropertyDefinition::CoverInitializedName(ident, expr) => { + if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) { + return None; + } + + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(ident.sym()), + default_init: Some(expr.clone()), + }); + } + } + } + + Some(ObjectPattern::new(bindings.into())) + } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.properties + .iter() + .any(PropertyDefinition::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.properties.iter().any(|prop| prop.contains(symbol)) + } +} + +impl ToIndentedString for ObjectLiteral { + fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { + let mut buf = "{\n".to_owned(); + let indentation = " ".repeat(indent_n + 1); + for property in self.properties().iter() { + buf.push_str(&match property { + PropertyDefinition::IdentifierReference(ident) => { + format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) + } + PropertyDefinition::Property(key, value) => { + format!( + "{indentation}{}: {},\n", + key.to_interned_string(interner), + value.to_no_indent_string(interner, indent_n + 1) + ) + } + PropertyDefinition::SpreadObject(key) => { + format!("{indentation}...{},\n", key.to_interned_string(interner)) + } + PropertyDefinition::MethodDefinition(key, method) => { + format!( + "{indentation}{}{}({}) {},\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + key.to_interned_string(interner), + match &method { + MethodDefinition::Get(expression) + | MethodDefinition::Set(expression) + | MethodDefinition::Ordinary(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::Generator(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::AsyncGenerator(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + MethodDefinition::Async(expression) => { + join_nodes(interner, expression.parameters().as_ref()) + } + }, + match &method { + MethodDefinition::Get(expression) + | MethodDefinition::Set(expression) + | MethodDefinition::Ordinary(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expression) => { + block_to_string(expression.body(), interner, indent_n + 1) + } + }, + ) + } + PropertyDefinition::CoverInitializedName(ident, expr) => { + format!( + "{indentation}{} = {},\n", + interner.resolve_expect(ident.sym()), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + }); + } + buf.push_str(&format!("{}}}", " ".repeat(indent_n))); + + buf + } +} + +impl From for ObjectLiteral +where + T: Into>, +{ + #[inline] + fn from(props: T) -> Self { + Self { + properties: props.into(), + } + } +} + +impl From for Expression { + #[inline] + fn from(obj: ObjectLiteral) -> Self { + Self::ObjectLiteral(obj) + } +} + +impl VisitWith for ObjectLiteral { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + for pd in self.properties.iter() { + try_break!(visitor.visit_property_definition(pd)); + } + ControlFlow::Continue(()) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + for pd in self.properties.iter_mut() { + try_break!(visitor.visit_property_definition_mut(pd)); + } + ControlFlow::Continue(()) + } +} diff --git a/boa_engine/src/syntax/ast/expression/literal/template.rs b/boa_ast/src/expression/literal/template.rs similarity index 79% rename from boa_engine/src/syntax/ast/expression/literal/template.rs rename to boa_ast/src/expression/literal/template.rs index cd1dc7c7ce..a0994daacd 100644 --- a/boa_engine/src/syntax/ast/expression/literal/template.rs +++ b/boa_ast/src/expression/literal/template.rs @@ -5,11 +5,11 @@ use std::borrow::Cow; use boa_interner::{Interner, Sym, ToInternedString}; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::{ - string::ToStringEscaped, - syntax::ast::{expression::Expression, ContainsSymbol}, + expression::Expression, try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + ContainsSymbol, ToStringEscaped, }; /// Template literals are string literals allowing embedded expressions. @@ -20,7 +20,7 @@ use crate::{ /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals /// [spec]: https://tc39.es/ecma262/#sec-template-literals -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct TemplateLiteral { elements: Box<[TemplateElement]>, @@ -39,7 +39,7 @@ impl From for Expression { /// node as the equivalent of the components found in a template literal. /// /// [spec]: https://tc39.es/ecma262/#sec-template-literals -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum TemplateElement { /// A simple string. @@ -51,11 +51,14 @@ pub enum TemplateElement { impl TemplateLiteral { /// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s. #[inline] + #[must_use] pub fn new(elements: Box<[TemplateElement]>) -> Self { Self { elements } } - pub(crate) fn elements(&self) -> &[TemplateElement] { + /// Gets the element list of this `TemplateLiteral`. + #[must_use] + pub fn elements(&self) -> &[TemplateElement] { &self.elements } @@ -142,35 +145,3 @@ impl VisitWith for TemplateElement { } } } - -#[cfg(test)] -mod tests { - use crate::exec; - - #[test] - fn template_literal() { - let scenario = r#" - let a = 10; - `result: ${a} and ${a+10}`; - "#; - - assert_eq!(&exec(scenario), "\"result: 10 and 20\""); - } - - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - function tag(t, ...args) { - let a = []; - a = a.concat([t[0], t[1], t[2]]); - a = a.concat([t.raw[0], t.raw[1], t.raw[2]]); - a = a.concat([args[0], args[1]]); - return a; - } - let a = 10; - tag`result: ${a} \x26 ${a + 10}`; - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/mod.rs b/boa_ast/src/expression/mod.rs similarity index 97% rename from boa_engine/src/syntax/ast/expression/mod.rs rename to boa_ast/src/expression/mod.rs index 75d74068a7..c229d0d9dd 100644 --- a/boa_engine/src/syntax/ast/expression/mod.rs +++ b/boa_ast/src/expression/mod.rs @@ -33,9 +33,9 @@ mod spread; mod tagged_template; mod r#yield; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; pub use call::{Call, SuperCall}; -pub use identifier::Identifier; +pub use identifier::{Identifier, RESERVED_IDENTIFIERS_STRICT}; pub use new::New; pub use optional::{Optional, OptionalOperation, OptionalOperationKind}; pub use r#await::Await; @@ -50,7 +50,7 @@ pub mod operator; /// The `Expression` Parse Node. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq)] pub enum Expression { /// The JavaScript `this` keyword refers to the object it belongs to. @@ -199,7 +199,8 @@ impl Expression { /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments // TODO: replace with a visitor #[inline] - pub(crate) fn contains_arguments(&self) -> bool { + #[must_use] + pub fn contains_arguments(&self) -> bool { match self { Expression::Identifier(ident) => *ident == Sym::ARGUMENTS, Expression::Function(_) @@ -239,7 +240,8 @@ impl Expression { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains // TODO: replace with a visitor - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + #[must_use] + pub fn contains(&self, symbol: ContainsSymbol) -> bool { match self { Expression::This => symbol == ContainsSymbol::This, Expression::Identifier(_) diff --git a/boa_engine/src/syntax/ast/expression/new.rs b/boa_ast/src/expression/new.rs similarity index 82% rename from boa_engine/src/syntax/ast/expression/new.rs rename to boa_ast/src/expression/new.rs index 9b8c0a7088..90fb74f833 100644 --- a/boa_engine/src/syntax/ast/expression/new.rs +++ b/boa_ast/src/expression/new.rs @@ -1,5 +1,5 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Call, ContainsSymbol}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Call, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; @@ -20,7 +20,7 @@ use super::Expression; /// /// [spec]: https://tc39.es/ecma262/#prod-NewExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct New { call: Call, @@ -29,18 +29,21 @@ pub struct New { impl New { /// Gets the constructor of the new expression. #[inline] + #[must_use] pub fn constructor(&self) -> &Expression { self.call.function() } /// Retrieves the arguments passed to the constructor. #[inline] - pub fn args(&self) -> &[Expression] { + #[must_use] + pub fn arguments(&self) -> &[Expression] { self.call.args() } /// Returns the inner call expression. - pub(crate) fn call(&self) -> &Call { + #[must_use] + pub fn call(&self) -> &Call { &self.call } @@ -91,16 +94,3 @@ impl VisitWith for New { visitor.visit_call_mut(&mut self.call) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - function MyClass() {} - let inst = new MyClass(); - "#, - ); - } -} diff --git a/boa_ast/src/expression/operator/assign/mod.rs b/boa_ast/src/expression/operator/assign/mod.rs new file mode 100644 index 0000000000..c11e7ba0e5 --- /dev/null +++ b/boa_ast/src/expression/operator/assign/mod.rs @@ -0,0 +1,207 @@ +//! Assignment expression nodes, as defined by the [spec]. +//! +//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right +//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple +//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=` +//! only allow ["simple"][simple] left hand side expressions as an assignment target. +//! +//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators +//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression +//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype + +mod op; + +use core::ops::ControlFlow; +pub use op::*; + +use boa_interner::{Interner, Sym, ToInternedString}; + +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ + expression::{access::PropertyAccess, identifier::Identifier, Expression}, + pattern::Pattern, + ContainsSymbol, +}; + +/// An assignment operator expression. +/// +/// See the [module level documentation][self] for more information. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct Assign { + op: AssignOp, + lhs: Box, + rhs: Box, +} + +impl Assign { + /// Creates an `Assign` AST Expression. + #[must_use] + pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self { + Self { + op, + lhs: Box::new(lhs), + rhs: Box::new(rhs), + } + } + + /// Gets the operator of the assignment operation. + #[inline] + #[must_use] + pub fn op(&self) -> AssignOp { + self.op + } + + /// Gets the left hand side of the assignment operation. + #[inline] + #[must_use] + pub fn lhs(&self) -> &AssignTarget { + &self.lhs + } + + /// Gets the right hand side of the assignment operation. + #[inline] + #[must_use] + pub fn rhs(&self) -> &Expression { + &self.rhs + } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + (match &*self.lhs { + AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS, + AssignTarget::Access(access) => access.contains_arguments(), + AssignTarget::Pattern(pattern) => pattern.contains_arguments(), + } || self.rhs.contains_arguments()) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + (match &*self.lhs { + AssignTarget::Identifier(_) => false, + AssignTarget::Access(access) => access.contains(symbol), + AssignTarget::Pattern(pattern) => pattern.contains(symbol), + } || self.rhs.contains(symbol)) + } +} + +impl ToInternedString for Assign { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} {} {}", + self.lhs.to_interned_string(interner), + self.op, + self.rhs.to_interned_string(interner) + ) + } +} + +impl From for Expression { + #[inline] + fn from(op: Assign) -> Self { + Self::Assign(op) + } +} + +impl VisitWith for Assign { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + try_break!(visitor.visit_assign_target(&self.lhs)); + visitor.visit_expression(&self.rhs) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + try_break!(visitor.visit_assign_target_mut(&mut self.lhs)); + visitor.visit_expression_mut(&mut self.rhs) + } +} + +/// The valid left-hand-side expressions of an assignment operator. Also called +/// [`LeftHandSideExpression`][spec] in the spec. +/// +/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum AssignTarget { + /// A simple identifier, such as `a`. + Identifier(Identifier), + /// A property access, such as `a.prop`. + Access(PropertyAccess), + /// A pattern assignment, such as `{a, b, ...c}`. + Pattern(Pattern), +} + +impl AssignTarget { + /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`]. + /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression. + #[must_use] + pub fn from_expression( + expression: &Expression, + strict: bool, + destructure: bool, + ) -> Option { + match expression { + Expression::Identifier(id) => Some(Self::Identifier(*id)), + Expression::PropertyAccess(access) => Some(Self::Access(access.clone())), + Expression::ObjectLiteral(object) if destructure => { + let pattern = object.to_pattern(strict)?; + Some(Self::Pattern(pattern.into())) + } + Expression::ArrayLiteral(array) if destructure => { + let pattern = array.to_pattern(strict)?; + Some(Self::Pattern(pattern.into())) + } + _ => None, + } + } +} + +impl ToInternedString for AssignTarget { + #[inline] + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + AssignTarget::Identifier(id) => id.to_interned_string(interner), + AssignTarget::Access(access) => access.to_interned_string(interner), + AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner), + } + } +} + +impl From for AssignTarget { + #[inline] + fn from(target: Identifier) -> Self { + Self::Identifier(target) + } +} + +impl VisitWith for AssignTarget { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + match self { + AssignTarget::Identifier(id) => visitor.visit_identifier(id), + AssignTarget::Access(pa) => visitor.visit_property_access(pa), + AssignTarget::Pattern(pat) => visitor.visit_pattern(pat), + } + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + match self { + AssignTarget::Identifier(id) => visitor.visit_identifier_mut(id), + AssignTarget::Access(pa) => visitor.visit_property_access_mut(pa), + AssignTarget::Pattern(pat) => visitor.visit_pattern_mut(pat), + } + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs b/boa_ast/src/expression/operator/assign/op.rs similarity index 99% rename from boa_engine/src/syntax/ast/expression/operator/assign/op.rs rename to boa_ast/src/expression/operator/assign/op.rs index 5900b2745d..8a815a022d 100644 --- a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs +++ b/boa_ast/src/expression/operator/assign/op.rs @@ -11,7 +11,7 @@ /// /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum AssignOp { /// The assignment operator assigns the value of the right operand to the left operand. diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs b/boa_ast/src/expression/operator/binary/mod.rs similarity index 91% rename from boa_engine/src/syntax/ast/expression/operator/binary/mod.rs rename to boa_ast/src/expression/operator/binary/mod.rs index d46ea962d4..15daa000e1 100644 --- a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs +++ b/boa_ast/src/expression/operator/binary/mod.rs @@ -21,14 +21,14 @@ pub use op::*; use boa_interner::{Interner, ToInternedString}; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, ContainsSymbol}; /// Binary operations require two operands, one before the operator and one after the operator. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Binary { op: BinaryOp, @@ -38,7 +38,8 @@ pub struct Binary { impl Binary { /// Creates a `BinOp` AST Expression. - pub(in crate::syntax) fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self { + #[must_use] + pub fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self { Self { op, lhs: Box::new(lhs), @@ -48,18 +49,21 @@ impl Binary { /// Gets the binary operation of the Expression. #[inline] + #[must_use] pub fn op(&self) -> BinaryOp { self.op } /// Gets the left hand side of the binary operation. #[inline] + #[must_use] pub fn lhs(&self) -> &Expression { &self.lhs } /// Gets the right hand side of the binary operation. #[inline] + #[must_use] pub fn rhs(&self) -> &Expression { &self.rhs } diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs b/boa_ast/src/expression/operator/binary/op.rs similarity index 98% rename from boa_engine/src/syntax/ast/expression/operator/binary/op.rs rename to boa_ast/src/expression/operator/binary/op.rs index cb6739b5f3..6430bb00ba 100644 --- a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs +++ b/boa_ast/src/expression/operator/binary/op.rs @@ -3,7 +3,7 @@ use std::fmt::{Display, Formatter, Result}; /// This represents a binary operation between two values. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BinaryOp { /// Numeric operation. @@ -85,7 +85,7 @@ impl Display for BinaryOp { /// - [MDN documentation][mdn] /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ArithmeticOp { /// The addition operator produces the sum of numeric operands or string concatenation. @@ -195,7 +195,7 @@ impl Display for ArithmeticOp { /// - [MDN documentation][mdn] /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BitwiseOp { /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1. @@ -318,7 +318,7 @@ impl Display for BitwiseOp { /// /// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RelationalOp { /// The equality operator converts the operands if they are not of the same type, then applies @@ -511,7 +511,7 @@ impl Display for RelationalOp { /// /// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LogicalOp { /// The logical AND operator returns the value of the first operand if it can be coerced into `false`; diff --git a/boa_engine/src/syntax/ast/expression/operator/conditional.rs b/boa_ast/src/expression/operator/conditional.rs similarity index 93% rename from boa_engine/src/syntax/ast/expression/operator/conditional.rs rename to boa_ast/src/expression/operator/conditional.rs index fc0f19edde..12983acbf3 100644 --- a/boa_engine/src/syntax/ast/expression/operator/conditional.rs +++ b/boa_ast/src/expression/operator/conditional.rs @@ -1,6 +1,6 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; @@ -18,7 +18,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Conditional { condition: Box, @@ -29,24 +29,28 @@ pub struct Conditional { impl Conditional { /// Gets the condition of the `Conditional` expression. #[inline] + #[must_use] pub fn condition(&self) -> &Expression { &self.condition } /// Gets the expression returned if `condition` is truthy. #[inline] + #[must_use] pub fn if_true(&self) -> &Expression { &self.if_true } /// Gets the expression returned if `condition` is falsy. #[inline] + #[must_use] pub fn if_false(&self) -> &Expression { &self.if_false } /// Creates a `Conditional` AST Expression. #[inline] + #[must_use] pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self { Self { condition: Box::new(condition), diff --git a/boa_engine/src/syntax/ast/expression/operator/mod.rs b/boa_ast/src/expression/operator/mod.rs similarity index 96% rename from boa_engine/src/syntax/ast/expression/operator/mod.rs rename to boa_ast/src/expression/operator/mod.rs index 4623c8ce99..2591935c1c 100644 --- a/boa_engine/src/syntax/ast/expression/operator/mod.rs +++ b/boa_ast/src/expression/operator/mod.rs @@ -18,6 +18,3 @@ pub mod binary; pub mod unary; pub use self::{assign::Assign, binary::Binary, conditional::Conditional, unary::Unary}; - -#[cfg(test)] -mod tests; diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs b/boa_ast/src/expression/operator/unary/mod.rs similarity index 90% rename from boa_engine/src/syntax/ast/expression/operator/unary/mod.rs rename to boa_ast/src/expression/operator/unary/mod.rs index baa262f940..0f29998195 100644 --- a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs +++ b/boa_ast/src/expression/operator/unary/mod.rs @@ -18,8 +18,8 @@ pub use op::*; use boa_interner::{Interner, ToInternedString}; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, ContainsSymbol}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, ContainsSymbol}; /// A unary expression is an operation with only one operand. /// @@ -29,7 +29,7 @@ use crate::syntax::ast::{expression::Expression, ContainsSymbol}; /// /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Unary { op: UnaryOp, @@ -38,7 +38,8 @@ pub struct Unary { impl Unary { /// Creates a new `UnaryOp` AST Expression. - pub(in crate::syntax) fn new(op: UnaryOp, target: Expression) -> Self { + #[must_use] + pub fn new(op: UnaryOp, target: Expression) -> Self { Self { op, target: Box::new(target), @@ -47,12 +48,14 @@ impl Unary { /// Gets the unary operation of the Expression. #[inline] + #[must_use] pub fn op(&self) -> UnaryOp { self.op } /// Gets the target of this unary operator. #[inline] + #[must_use] pub fn target(&self) -> &Expression { self.target.as_ref() } diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs b/boa_ast/src/expression/operator/unary/op.rs similarity index 99% rename from boa_engine/src/syntax/ast/expression/operator/unary/op.rs rename to boa_ast/src/expression/operator/unary/op.rs index a14a634d55..63527dce23 100644 --- a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs +++ b/boa_ast/src/expression/operator/unary/op.rs @@ -10,7 +10,7 @@ /// /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum UnaryOp { /// The increment operator increments (adds one to) its operand and returns a value. diff --git a/boa_engine/src/syntax/ast/expression/optional.rs b/boa_ast/src/expression/optional.rs similarity index 95% rename from boa_engine/src/syntax/ast/expression/optional.rs rename to boa_ast/src/expression/optional.rs index 4919578ac7..42f668bee5 100644 --- a/boa_engine/src/syntax/ast/expression/optional.rs +++ b/boa_ast/src/expression/optional.rs @@ -1,14 +1,14 @@ use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{join_nodes, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{join_nodes, ContainsSymbol}; use super::{access::PropertyAccessField, Expression}; /// List of valid operations in an [`Optional`] chain. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum OptionalOperationKind { /// A property access (`a?.prop`). @@ -91,7 +91,7 @@ impl VisitWith for OptionalOperationKind { /// (`?.item`) will force the expression to return `undefined` if the target is `undefined` or `null`. /// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target /// is `undefined` or `null`. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct OptionalOperation { kind: OptionalOperationKind, @@ -101,11 +101,13 @@ pub struct OptionalOperation { impl OptionalOperation { /// Creates a new `OptionalOperation`. #[inline] + #[must_use] pub fn new(kind: OptionalOperationKind, shorted: bool) -> Self { Self { kind, shorted } } /// Gets the kind of operation. #[inline] + #[must_use] pub fn kind(&self) -> &OptionalOperationKind { &self.kind } @@ -113,6 +115,7 @@ impl OptionalOperation { /// Returns `true` if the operation short-circuits the [`Optional`] chain when the target is /// `undefined` or `null`. #[inline] + #[must_use] pub fn shorted(&self) -> bool { self.shorted } @@ -200,7 +203,7 @@ impl VisitWith for OptionalOperation { /// /// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Optional { target: Box, @@ -234,6 +237,7 @@ impl VisitWith for Optional { impl Optional { /// Creates a new `Optional` expression. #[inline] + #[must_use] pub fn new(target: Expression, chain: Box<[OptionalOperation]>) -> Self { Self { target: Box::new(target), @@ -243,12 +247,14 @@ impl Optional { /// Gets the target of this `Optional` expression. #[inline] + #[must_use] pub fn target(&self) -> &Expression { self.target.as_ref() } /// Gets the chain of accesses and calls that will be applied to the target at runtime. #[inline] + #[must_use] pub fn chain(&self) -> &[OptionalOperation] { self.chain.as_ref() } diff --git a/boa_engine/src/syntax/ast/expression/spread.rs b/boa_ast/src/expression/spread.rs similarity index 66% rename from boa_engine/src/syntax/ast/expression/spread.rs rename to boa_ast/src/expression/spread.rs index 5dc32d36ba..68805d08a0 100644 --- a/boa_engine/src/syntax/ast/expression/spread.rs +++ b/boa_ast/src/expression/spread.rs @@ -1,8 +1,8 @@ use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::ContainsSymbol; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::ContainsSymbol; use super::Expression; @@ -22,8 +22,8 @@ use super::Expression; /// /// [spec]: https://tc39.es/ecma262/#prod-SpreadElement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "deser", serde(transparent))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] #[derive(Clone, Debug, PartialEq)] pub struct Spread { target: Box, @@ -32,12 +32,14 @@ pub struct Spread { impl Spread { /// Gets the target expression to be expanded by the spread operator. #[inline] + #[must_use] pub fn target(&self) -> &Expression { &self.target } /// Creates a `Spread` AST Expression. #[inline] + #[must_use] pub fn new(target: Expression) -> Self { Self { target: Box::new(target), @@ -84,54 +86,3 @@ impl VisitWith for Spread { visitor.visit_expression_mut(&mut self.target) } } - -#[cfg(test)] -mod tests { - use crate::exec; - - #[test] - fn spread_with_new() { - let scenario = r#" - function F(m) { - this.m = m; - } - function f(...args) { - return new F(...args); - } - let a = f('message'); - a.m; - "#; - assert_eq!(&exec(scenario), r#""message""#); - } - - #[test] - fn spread_with_call() { - let scenario = r#" - function f(m) { - return m; - } - function g(...args) { - return f(...args); - } - let a = g('message'); - a; - "#; - assert_eq!(&exec(scenario), r#""message""#); - } - - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - function f(m) { - return m; - } - function g(...args) { - return f(...args); - } - let a = g("message"); - a; - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/tagged_template.rs b/boa_ast/src/expression/tagged_template.rs similarity index 79% rename from boa_engine/src/syntax/ast/expression/tagged_template.rs rename to boa_ast/src/expression/tagged_template.rs index 78b1f8f45c..0557d32e76 100644 --- a/boa_engine/src/syntax/ast/expression/tagged_template.rs +++ b/boa_ast/src/expression/tagged_template.rs @@ -1,9 +1,9 @@ use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::ContainsSymbol; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::ContainsSymbol; use super::Expression; @@ -14,7 +14,7 @@ use super::Expression; /// /// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates /// [spec]: https://tc39.es/ecma262/#sec-tagged-templates -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct TaggedTemplate { tag: Box, @@ -27,6 +27,7 @@ impl TaggedTemplate { /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and /// the expressions. #[inline] + #[must_use] pub fn new( tag: Expression, raws: Box<[Sym]>, @@ -41,23 +42,31 @@ impl TaggedTemplate { } } + /// Gets the tag function of the template. #[inline] - pub(crate) fn tag(&self) -> &Expression { + #[must_use] + pub fn tag(&self) -> &Expression { &self.tag } + /// Gets the inner raw strings of the template. #[inline] - pub(crate) fn raws(&self) -> &[Sym] { + #[must_use] + pub fn raws(&self) -> &[Sym] { &self.raws } + /// Gets the cooked strings of the template. #[inline] - pub(crate) fn cookeds(&self) -> &[Option] { + #[must_use] + pub fn cookeds(&self) -> &[Option] { &self.cookeds } + /// Gets the interpolated expressions of the template. #[inline] - pub(crate) fn exprs(&self) -> &[Expression] { + #[must_use] + pub fn exprs(&self) -> &[Expression] { &self.exprs } @@ -131,26 +140,3 @@ impl VisitWith for TaggedTemplate { ControlFlow::Continue(()) } } - -#[cfg(test)] -mod tests { - #[test] - fn tagged_template() { - let scenario = r#" - function tag(t, ...args) { - let a = [] - a = a.concat([t[0], t[1], t[2]]); - a = a.concat([t.raw[0], t.raw[1], t.raw[2]]); - a = a.concat([args[0], args[1]]); - return a - } - let a = 10; - tag`result: ${a} \x26 ${a+10}`; - "#; - - assert_eq!( - &crate::exec(scenario), - r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"# - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/yield.rs b/boa_ast/src/expression/yield.rs similarity index 93% rename from boa_engine/src/syntax/ast/expression/yield.rs rename to boa_ast/src/expression/yield.rs index 6284f316fc..01e67d2ed3 100644 --- a/boa_engine/src/syntax/ast/expression/yield.rs +++ b/boa_ast/src/expression/yield.rs @@ -1,8 +1,8 @@ use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::ContainsSymbol; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::ContainsSymbol; use super::Expression; @@ -14,7 +14,7 @@ use super::Expression; /// /// [spec]: https://tc39.es/ecma262/#prod-YieldExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Yield { target: Option>, @@ -30,12 +30,14 @@ impl Yield { /// Returns `true` if this `Yield` statement delegates to another generator or iterable object. #[inline] + #[must_use] pub fn delegate(&self) -> bool { self.delegate } /// Creates a `Yield` AST Expression. #[inline] + #[must_use] pub fn new(expr: Option, delegate: bool) -> Self { Self { target: expr.map(Box::new), diff --git a/boa_engine/src/syntax/ast/function/arrow_function.rs b/boa_ast/src/function/arrow_function.rs similarity index 81% rename from boa_engine/src/syntax/ast/function/arrow_function.rs rename to boa_ast/src/function/arrow_function.rs index ad2003d7eb..c7cb7e88e7 100644 --- a/boa_engine/src/syntax/ast/function/arrow_function.rs +++ b/boa_ast/src/function/arrow_function.rs @@ -1,9 +1,9 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ expression::{Expression, Identifier}, join_nodes, ContainsSymbol, StatementList, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; @@ -18,7 +18,7 @@ use super::FormalParameterList; /// /// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ArrowFunction { name: Option, @@ -29,11 +29,8 @@ pub struct ArrowFunction { impl ArrowFunction { /// Creates a new `ArrowFunctionDecl` AST Expression. #[inline] - pub(in crate::syntax) fn new( - name: Option, - params: FormalParameterList, - body: StatementList, - ) -> Self { + #[must_use] + pub fn new(name: Option, params: FormalParameterList, body: StatementList) -> Self { Self { name, parameters: params, @@ -43,6 +40,7 @@ impl ArrowFunction { /// Gets the name of the function declaration. #[inline] + #[must_use] pub fn name(&self) -> Option { self.name } @@ -55,13 +53,15 @@ impl ArrowFunction { /// Gets the list of parameters of the arrow function. #[inline] - pub(crate) fn parameters(&self) -> &FormalParameterList { + #[must_use] + pub fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the arrow function. #[inline] - pub(crate) fn body(&self) -> &StatementList { + #[must_use] + pub fn body(&self) -> &StatementList { &self.body } @@ -88,7 +88,7 @@ impl ArrowFunction { impl ToIndentedString for ArrowFunction { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { - let mut buf = format!("({}", join_nodes(interner, &self.parameters.parameters)); + let mut buf = format!("({}", join_nodes(interner, self.parameters.as_ref())); if self.body().statements().is_empty() { buf.push_str(") => {}"); } else { @@ -131,19 +131,3 @@ impl VisitWith for ArrowFunction { visitor.visit_statement_list_mut(&mut self.body) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let arrow_func = (a, b) => { - console.log("in multi statement arrow"); - console.log(b); - }; - let arrow_func_2 = (a, b) => {}; - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/function/async_function.rs b/boa_ast/src/function/async_function.rs similarity index 82% rename from boa_engine/src/syntax/ast/function/async_function.rs rename to boa_ast/src/function/async_function.rs index 867c8fb8da..556a02c016 100644 --- a/boa_engine/src/syntax/ast/function/async_function.rs +++ b/boa_ast/src/function/async_function.rs @@ -1,11 +1,11 @@ //! Async Function Expression. -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ expression::{Expression, Identifier}, join_nodes, Declaration, StatementList, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; @@ -19,7 +19,7 @@ use super::FormalParameterList; /// /// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct AsyncFunction { name: Option, @@ -30,7 +30,8 @@ pub struct AsyncFunction { impl AsyncFunction { /// Creates a new function expression #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -44,18 +45,21 @@ impl AsyncFunction { /// Gets the name of the function declaration. #[inline] + #[must_use] pub fn name(&self) -> Option { self.name } /// Gets the list of parameters of the function declaration. #[inline] + #[must_use] pub fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the function declaration. #[inline] + #[must_use] pub fn body(&self) -> &StatementList { &self.body } @@ -69,7 +73,7 @@ impl ToIndentedString for AsyncFunction { } buf.push_str(&format!( "({}", - join_nodes(interner, &self.parameters.parameters) + join_nodes(interner, self.parameters.as_ref()) )); if self.body().statements().is_empty() { buf.push_str(") {}"); @@ -121,22 +125,3 @@ impl VisitWith for AsyncFunction { visitor.visit_statement_list_mut(&mut self.body) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - async function async_func(a, b) { - console.log(a); - } - async function async_func_2(a, b) {} - pass_async_func(async function(a, b) { - console.log("in async callback", a); - }); - pass_async_func(async function(a, b) {}); - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/function/async_generator.rs b/boa_ast/src/function/async_generator.rs similarity index 92% rename from boa_engine/src/syntax/ast/function/async_generator.rs rename to boa_ast/src/function/async_generator.rs index 93db559e5b..fe85194aad 100644 --- a/boa_engine/src/syntax/ast/function/async_generator.rs +++ b/boa_ast/src/function/async_generator.rs @@ -1,11 +1,11 @@ //! Async Generator Expression -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ block_to_string, expression::{Expression, Identifier}, join_nodes, Declaration, StatementList, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; @@ -18,7 +18,7 @@ use super::FormalParameterList; /// /// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function* -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct AsyncGenerator { name: Option, @@ -29,7 +29,8 @@ pub struct AsyncGenerator { impl AsyncGenerator { /// Creates a new async generator expression #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -43,18 +44,21 @@ impl AsyncGenerator { /// Gets the name of the async generator expression #[inline] + #[must_use] pub fn name(&self) -> Option { self.name } /// Gets the list of parameters of the async generator expression #[inline] + #[must_use] pub fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the async generator expression #[inline] + #[must_use] pub fn body(&self) -> &StatementList { &self.body } @@ -68,7 +72,7 @@ impl ToIndentedString for AsyncGenerator { } buf.push_str(&format!( "({}) {}", - join_nodes(interner, &self.parameters.parameters), + join_nodes(interner, self.parameters.as_ref()), block_to_string(&self.body, interner, indentation) )); diff --git a/boa_engine/src/syntax/ast/function/class.rs b/boa_ast/src/function/class.rs similarity index 83% rename from boa_engine/src/syntax/ast/function/class.rs rename to boa_ast/src/function/class.rs index 302313f803..ba2586a2bd 100644 --- a/boa_engine/src/syntax/ast/function/class.rs +++ b/boa_ast/src/function/class.rs @@ -1,17 +1,14 @@ use core::ops::ControlFlow; use std::borrow::Cow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::{ - string::ToStringEscaped, - syntax::ast::{ - block_to_string, - expression::{Expression, Identifier}, - join_nodes, - property::{MethodDefinition, PropertyName}, - ContainsSymbol, Declaration, StatementList, StatementListItem, - }, + block_to_string, + expression::{Expression, Identifier}, + join_nodes, + property::{MethodDefinition, PropertyName}, try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + ContainsSymbol, Declaration, StatementList, StatementListItem, ToStringEscaped, }; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; @@ -24,7 +21,7 @@ use super::Function; /// /// [spec]: https://tc39.es/ecma262/#sec-class-definitions /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Class { name: Option, @@ -36,7 +33,8 @@ pub struct Class { impl Class { /// Creates a new class declaration. #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( name: Option, super_ref: Option, constructor: Option, @@ -52,25 +50,29 @@ impl Class { /// Returns the name of the class. #[inline] - pub(crate) fn name(&self) -> Option { + #[must_use] + pub fn name(&self) -> Option { self.name } /// Returns the super class ref of the class. #[inline] - pub(crate) fn super_ref(&self) -> Option<&Expression> { + #[must_use] + pub fn super_ref(&self) -> Option<&Expression> { self.super_ref.as_ref() } /// Returns the constructor of the class. #[inline] - pub(crate) fn constructor(&self) -> Option<&Function> { + #[must_use] + pub fn constructor(&self) -> Option<&Function> { self.constructor.as_ref() } /// Gets the list of all fields defined on the class. #[inline] - pub(crate) fn elements(&self) -> &[ClassElement] { + #[must_use] + pub fn elements(&self) -> &[ClassElement] { &self.elements } @@ -126,7 +128,7 @@ impl ToIndentedString for Class { if let Some(expr) = &self.constructor { buf.push_str(&format!( "{indentation}constructor({}) {}\n", - join_nodes(interner, &expr.parameters().parameters), + join_nodes(interner, expr.parameters().as_ref()), block_to_string(expr.body(), interner, indent_n + 1) )); } @@ -145,16 +147,16 @@ impl ToIndentedString for Class { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) | MethodDefinition::Ordinary(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Generator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::AsyncGenerator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Async(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } }, match &method { @@ -188,16 +190,16 @@ impl ToIndentedString for Class { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) | MethodDefinition::Ordinary(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Generator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::AsyncGenerator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Async(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } }, match &method { @@ -258,16 +260,16 @@ impl ToIndentedString for Class { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) | MethodDefinition::Ordinary(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Generator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::AsyncGenerator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Async(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } }, match &method { @@ -301,16 +303,16 @@ impl ToIndentedString for Class { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) | MethodDefinition::Ordinary(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Generator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::AsyncGenerator(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } MethodDefinition::Async(expr) => { - join_nodes(interner, &expr.parameters().parameters) + join_nodes(interner, expr.parameters().as_ref()) } }, match &method { @@ -423,7 +425,7 @@ impl VisitWith for Class { /// An element that can be within a [`Class`], as defined by the [spec]. /// /// [spec]: https://tc39.es/ecma262/#prod-ClassElement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum ClassElement { /// A method definition, including `get` and `set` accessors. @@ -564,113 +566,3 @@ impl VisitWith for ClassElement { } } } - -#[cfg(test)] -mod tests { - use crate::syntax::ast::test_formatting; - - #[test] - fn class_declaration_empty() { - test_formatting( - r#" - class A {} - "#, - ); - } - - #[test] - fn class_declaration_empty_extends() { - test_formatting( - r#" - class A extends Object {} - "#, - ); - } - - #[test] - fn class_declaration_constructor() { - test_formatting( - r#" - class A { - constructor(a, b, c) { - this.value = a + b + c; - } - } - "#, - ); - } - - #[test] - fn class_declaration_elements() { - test_formatting( - r#" - class A { - a; - b = 1; - c() {} - d(a, b, c) { - return a + b + c; - } - set e(value) {} - get e() {} - set(a, b) {} - get(a, b) {} - } - "#, - ); - } - - #[test] - fn class_declaration_elements_private() { - test_formatting( - r#" - class A { - #a; - #b = 1; - #c() {} - #d(a, b, c) { - return a + b + c; - } - set #e(value) {} - get #e() {} - } - "#, - ); - } - - #[test] - fn class_declaration_elements_static() { - test_formatting( - r#" - class A { - static a; - static b = 1; - static c() {} - static d(a, b, c) { - return a + b + c; - } - static set e(value) {} - static get e() {} - } - "#, - ); - } - - #[test] - fn class_declaration_elements_private_static() { - test_formatting( - r#" - class A { - static #a; - static #b = 1; - static #c() {} - static #d(a, b, c) { - return a + b + c; - } - static set #e(value) {} - static get #e() {} - } - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/function/generator.rs b/boa_ast/src/function/generator.rs similarity index 92% rename from boa_engine/src/syntax/ast/function/generator.rs rename to boa_ast/src/function/generator.rs index 89f240d009..bae097453d 100644 --- a/boa_engine/src/syntax/ast/function/generator.rs +++ b/boa_ast/src/function/generator.rs @@ -1,12 +1,12 @@ -use crate::syntax::ast::{ +use crate::{ block_to_string, expression::{Expression, Identifier}, join_nodes, Declaration, StatementList, }; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, ToIndentedString}; use super::FormalParameterList; @@ -20,7 +20,7 @@ use super::FormalParameterList; /// /// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Generator { name: Option, @@ -31,7 +31,8 @@ pub struct Generator { impl Generator { /// Creates a new generator expression #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -45,18 +46,21 @@ impl Generator { /// Gets the name of the generator declaration. #[inline] + #[must_use] pub fn name(&self) -> Option { self.name } /// Gets the list of parameters of the generator declaration. #[inline] + #[must_use] pub fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the generator declaration. #[inline] + #[must_use] pub fn body(&self) -> &StatementList { &self.body } @@ -70,7 +74,7 @@ impl ToIndentedString for Generator { } buf.push_str(&format!( "({}) {}", - join_nodes(interner, &self.parameters.parameters), + join_nodes(interner, self.parameters.as_ref()), block_to_string(&self.body, interner, indentation) )); diff --git a/boa_engine/src/syntax/ast/function/mod.rs b/boa_ast/src/function/mod.rs similarity index 65% rename from boa_engine/src/syntax/ast/function/mod.rs rename to boa_ast/src/function/mod.rs index f9e6c2f90c..34d0eede73 100644 --- a/boa_engine/src/syntax/ast/function/mod.rs +++ b/boa_ast/src/function/mod.rs @@ -33,17 +33,15 @@ pub use async_generator::AsyncGenerator; pub use class::{Class, ClassElement}; use core::ops::ControlFlow; pub use generator::Generator; -pub use parameters::{FormalParameter, FormalParameterList}; +pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags}; -pub(crate) use parameters::FormalParameterListFlags; - -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{block_to_string, join_nodes, StatementList}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{block_to_string, join_nodes, StatementList}; use boa_interner::{Interner, ToIndentedString}; use super::expression::{Expression, Identifier}; -use super::{ContainsSymbol, Declaration}; +use super::Declaration; /// A function definition, as defined by the [spec]. /// @@ -55,7 +53,7 @@ use super::{ContainsSymbol, Declaration}; /// /// [spec]: https://tc39.es/ecma262/#sec-function-definitions /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Function { name: Option, @@ -66,7 +64,8 @@ pub struct Function { impl Function { /// Creates a new function expression #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( name: Option, parameters: FormalParameterList, body: StatementList, @@ -80,18 +79,21 @@ impl Function { /// Gets the name of the function declaration. #[inline] + #[must_use] pub fn name(&self) -> Option { self.name } /// Gets the list of parameters of the function declaration. #[inline] + #[must_use] pub fn parameters(&self) -> &FormalParameterList { &self.parameters } /// Gets the body of the function declaration. #[inline] + #[must_use] pub fn body(&self) -> &StatementList { &self.body } @@ -105,7 +107,7 @@ impl ToIndentedString for Function { } buf.push_str(&format!( "({}) {}", - join_nodes(interner, &self.parameters.parameters), + join_nodes(interner, self.parameters.as_ref()), block_to_string(&self.body, interner, indentation) )); @@ -127,47 +129,6 @@ impl From for Declaration { } } -/// Helper function to check if a function contains a super call or super property access. -pub(crate) fn function_contains_super( - body: &StatementList, - parameters: &FormalParameterList, -) -> bool { - for param in parameters.parameters.iter() { - if param.variable().contains(ContainsSymbol::SuperCall) - || param.variable().contains(ContainsSymbol::SuperProperty) - { - return true; - } - } - for node in body.statements() { - if node.contains(ContainsSymbol::SuperCall) || node.contains(ContainsSymbol::SuperProperty) - { - return true; - } - } - false -} - -/// Returns `true` if the function parameters or body contain a direct `super` call. -/// -/// More information: -/// - [ECMAScript specification][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper -pub(crate) fn has_direct_super(body: &StatementList, parameters: &FormalParameterList) -> bool { - for param in parameters.parameters.iter() { - if param.variable().contains(ContainsSymbol::SuperCall) { - return true; - } - } - for node in body.statements() { - if node.contains(ContainsSymbol::SuperCall) { - return true; - } - } - false -} - impl VisitWith for Function { fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow where @@ -191,34 +152,3 @@ impl VisitWith for Function { visitor.visit_statement_list_mut(&mut self.body) } } - -#[cfg(test)] -mod tests { - - #[test] - fn duplicate_function_name() { - let scenario = r#" - function f () {} - function f () {return 12;} - f() - "#; - - assert_eq!(&crate::exec(scenario), "12"); - } - - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - function func(a, b) { - console.log(a); - } - function func_2(a, b) {} - pass_func(function(a, b) { - console.log("in callback", a); - }); - pass_func(function(a, b) {}); - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/function/parameters.rs b/boa_ast/src/function/parameters.rs similarity index 72% rename from boa_engine/src/syntax/ast/function/parameters.rs rename to boa_ast/src/function/parameters.rs index 9386ec5bec..512c785c2e 100644 --- a/boa_engine/src/syntax/ast/function/parameters.rs +++ b/boa_ast/src/function/parameters.rs @@ -1,14 +1,11 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::{ - ast::{ - declaration::{Binding, Variable}, - expression::{Expression, Identifier}, - pattern::Pattern, - ContainsSymbol, Position, - }, - parser::ParseError, +use crate::{ + declaration::{Binding, Variable}, + expression::{Expression, Identifier}, + pattern::Pattern, + try_break, + visitor::{VisitWith, Visitor, VisitorMut}, + ContainsSymbol, }; -use crate::try_break; use bitflags::bitflags; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; @@ -17,30 +14,18 @@ use rustc_hash::FxHashSet; /// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec]. /// /// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct FormalParameterList { - pub(crate) parameters: Box<[FormalParameter]>, - pub(crate) flags: FormalParameterListFlags, - pub(crate) length: u32, + parameters: Box<[FormalParameter]>, + flags: FormalParameterListFlags, + length: u32, } impl FormalParameterList { - /// Creates a new formal parameter list. - pub(crate) fn new( - parameters: Box<[FormalParameter]>, - flags: FormalParameterListFlags, - length: u32, - ) -> Self { - Self { - parameters, - flags, - length, - } - } - /// Creates a new empty formal parameter list. - pub(crate) fn empty() -> Self { + #[must_use] + pub fn new() -> Self { Self { parameters: Box::new([]), flags: FormalParameterListFlags::default(), @@ -48,61 +33,101 @@ impl FormalParameterList { } } + /// Creates a `FormalParameterList` from a list of [`FormalParameter`]s. + #[must_use] + pub fn from_parameters(parameters: Vec) -> Self { + let mut flags = FormalParameterListFlags::default(); + let mut length = 0; + let mut names = FxHashSet::default(); + + for parameter in ¶meters { + let parameter_names = parameter.names(); + + for name in parameter_names { + if name == Sym::ARGUMENTS { + flags |= FormalParameterListFlags::HAS_ARGUMENTS; + } + if names.contains(&name) { + flags |= FormalParameterListFlags::HAS_DUPLICATES; + } else { + names.insert(name); + } + } + + if parameter.is_rest_param() { + flags |= FormalParameterListFlags::HAS_REST_PARAMETER; + } + if parameter.init().is_some() { + flags |= FormalParameterListFlags::HAS_EXPRESSIONS; + } + if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() + { + flags.remove(FormalParameterListFlags::IS_SIMPLE); + } + if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS) + || parameter.is_rest_param() + || parameter.init().is_some()) + { + length += 1; + } + } + + Self { + parameters: parameters.into(), + flags, + length, + } + } + /// Returns the length of the parameter list. /// Note that this is not equal to the length of the parameters slice. - pub(crate) fn length(&self) -> u32 { + #[must_use] + pub fn length(&self) -> u32 { self.length } + /// Returns the parameter list flags. + #[must_use] + pub fn flags(&self) -> FormalParameterListFlags { + self.flags + } + /// Indicates if the parameter list is simple. - pub(crate) fn is_simple(&self) -> bool { + #[must_use] + pub fn is_simple(&self) -> bool { self.flags.contains(FormalParameterListFlags::IS_SIMPLE) } /// Indicates if the parameter list has duplicate parameters. - pub(crate) fn has_duplicates(&self) -> bool { + #[must_use] + pub fn has_duplicates(&self) -> bool { self.flags .contains(FormalParameterListFlags::HAS_DUPLICATES) } /// Indicates if the parameter list has a rest parameter. - pub(crate) fn has_rest_parameter(&self) -> bool { + #[must_use] + pub fn has_rest_parameter(&self) -> bool { self.flags .contains(FormalParameterListFlags::HAS_REST_PARAMETER) } /// Indicates if the parameter list has expressions in it's parameters. - pub(crate) fn has_expressions(&self) -> bool { + #[must_use] + pub fn has_expressions(&self) -> bool { self.flags .contains(FormalParameterListFlags::HAS_EXPRESSIONS) } /// Indicates if the parameter list has parameters named 'arguments'. - pub(crate) fn has_arguments(&self) -> bool { + #[must_use] + pub fn has_arguments(&self) -> bool { self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS) } - /// Helper to check if any parameter names are declared in the given list. - pub(crate) fn name_in_lexically_declared_names( - &self, - names: &[Identifier], - position: Position, - ) -> Result<(), ParseError> { - for parameter in self.parameters.iter() { - for name in ¶meter.names() { - if names.contains(name) { - return Err(ParseError::General { - message: "formal parameter declared in lexically declared names", - position, - }); - } - } - } - Ok(()) - } - /// Check if the any of the parameters contains a yield expression. - pub(crate) fn contains_yield_expression(&self) -> bool { + #[must_use] + pub fn contains_yield_expression(&self) -> bool { for parameter in self.parameters.iter() { if parameter .variable() @@ -115,7 +140,8 @@ impl FormalParameterList { } /// Check if the any of the parameters contains a await expression. - pub(crate) fn contains_await_expression(&self) -> bool { + #[must_use] + pub fn contains_await_expression(&self) -> bool { for parameter in self.parameters.iter() { if parameter .variable() @@ -142,75 +168,19 @@ impl FormalParameterList { impl From> for FormalParameterList { fn from(parameters: Vec) -> Self { - let mut flags = FormalParameterListFlags::default(); - let mut length = 0; - let mut names = FxHashSet::default(); - - for parameter in ¶meters { - let parameter_names = parameter.names(); - - for name in parameter_names { - if name == Sym::ARGUMENTS { - flags |= FormalParameterListFlags::HAS_ARGUMENTS; - } - if names.contains(&name) { - flags |= FormalParameterListFlags::HAS_DUPLICATES; - } else { - names.insert(name); - } - } - - if parameter.is_rest_param() { - flags |= FormalParameterListFlags::HAS_REST_PARAMETER; - } - if parameter.init().is_some() { - flags |= FormalParameterListFlags::HAS_EXPRESSIONS; - } - if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() - { - flags.remove(FormalParameterListFlags::IS_SIMPLE); - } - if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS) - || parameter.is_rest_param() - || parameter.init().is_some()) - { - length += 1; - } - } - - Self { - parameters: parameters.into_boxed_slice(), - flags, - length, - } + Self::from_parameters(parameters) } } impl From for FormalParameterList { fn from(parameter: FormalParameter) -> Self { - let mut flags = FormalParameterListFlags::default(); - if parameter.is_rest_param() { - flags |= FormalParameterListFlags::HAS_REST_PARAMETER; - } - if parameter.init().is_some() { - flags |= FormalParameterListFlags::HAS_EXPRESSIONS; - } - if parameter.names().contains(&Sym::ARGUMENTS.into()) { - flags |= FormalParameterListFlags::HAS_ARGUMENTS; - } - if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() { - flags.remove(FormalParameterListFlags::IS_SIMPLE); - } - let length = if parameter.is_rest_param() || parameter.init().is_some() { - 0 - } else { - 1 - }; - Self { - parameters: Box::new([parameter]), - flags, - length, - } + Self::from_parameters(vec![parameter]) + } +} + +impl AsRef<[FormalParameter]> for FormalParameterList { + fn as_ref(&self) -> &[FormalParameter] { + &self.parameters } } @@ -240,12 +210,17 @@ impl VisitWith for FormalParameterList { bitflags! { /// Flags for a [`FormalParameterList`]. #[allow(clippy::unsafe_derive_deserialize)] - #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] - pub(crate) struct FormalParameterListFlags: u8 { + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct FormalParameterListFlags: u8 { + /// Has only identifier parameters with no initialization expressions. const IS_SIMPLE = 0b0000_0001; + /// Has any duplicate parameters. const HAS_DUPLICATES = 0b0000_0010; + /// Has a rest parameter. const HAS_REST_PARAMETER = 0b0000_0100; + /// Has any initialization expression. const HAS_EXPRESSIONS = 0b0000_1000; + /// Has an argument with the name `arguments`. const HAS_ARGUMENTS = 0b0001_0000; } } @@ -271,7 +246,7 @@ impl Default for FormalParameterListFlags { /// /// [spec]: https://tc39.es/ecma262/#prod-FormalParameter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct FormalParameter { variable: Variable, @@ -280,7 +255,7 @@ pub struct FormalParameter { impl FormalParameter { /// Creates a new formal parameter. - pub(in crate::syntax) fn new(variable: D, is_rest_param: bool) -> Self + pub fn new(variable: D, is_rest_param: bool) -> Self where D: Into, { @@ -291,6 +266,7 @@ impl FormalParameter { } /// Gets the name of the formal parameter. + #[must_use] pub fn names(&self) -> Vec { match self.variable.binding() { Binding::Identifier(ident) => vec![*ident], @@ -303,21 +279,25 @@ impl FormalParameter { } /// Gets the variable of the formal parameter + #[must_use] pub fn variable(&self) -> &Variable { &self.variable } /// Gets the initialization node of the formal parameter, if any. + #[must_use] pub fn init(&self) -> Option<&Expression> { self.variable.init() } /// Returns `true` if the parameter is a rest parameter. + #[must_use] pub fn is_rest_param(&self) -> bool { self.is_rest_param } /// Returns `true` if the parameter is a simple [`Identifier`]. + #[must_use] pub fn is_identifier(&self) -> bool { matches!(&self.variable.binding(), Binding::Identifier(_)) } diff --git a/boa_engine/src/syntax/ast/keyword.rs b/boa_ast/src/keyword.rs similarity index 99% rename from boa_engine/src/syntax/ast/keyword.rs rename to boa_ast/src/keyword.rs index 9c605e3d6f..a59527929b 100644 --- a/boa_engine/src/syntax/ast/keyword.rs +++ b/boa_ast/src/keyword.rs @@ -9,17 +9,15 @@ //! [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords -use crate::{ - string::utf16, - syntax::ast::expression::operator::binary::{BinaryOp, RelationalOp}, -}; +use crate::expression::operator::binary::{BinaryOp, RelationalOp}; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; use std::{convert::TryFrom, error, fmt, str::FromStr}; /// List of keywords recognized by the JavaScript grammar. /// /// See the [module-level documentation][self] for more details. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Keyword { /// The `await` keyword. @@ -479,6 +477,7 @@ pub enum Keyword { impl Keyword { /// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof` /// keywords. + #[must_use] pub fn as_binary_op(self) -> Option { match self { Self::In => Some(BinaryOp::Relational(RelationalOp::In)), @@ -488,6 +487,7 @@ impl Keyword { } /// Gets the keyword as a tuple of strings. + #[must_use] pub fn as_str(self) -> (&'static str, &'static [u16]) { match self { Self::Await => ("await", utf16!("await")), diff --git a/boa_engine/src/syntax/ast/mod.rs b/boa_ast/src/lib.rs similarity index 50% rename from boa_engine/src/syntax/ast/mod.rs rename to boa_ast/src/lib.rs index 6f4d3e87f4..2978d55c1b 100644 --- a/boa_engine/src/syntax/ast/mod.rs +++ b/boa_ast/src/lib.rs @@ -1,6 +1,6 @@ //! The Javascript Abstract Syntax Tree. //! -//! This module contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript spec. +//! This crate contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript spec. //! Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions are //! only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST itself. //! @@ -11,7 +11,49 @@ //! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar //! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules -#![deny(missing_docs)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn( + clippy::perf, + clippy::single_match_else, + clippy::dbg_macro, + clippy::doc_markdown, + clippy::wildcard_imports, + clippy::struct_excessive_bools, + clippy::doc_markdown, + clippy::semicolon_if_nothing_returned, + clippy::pedantic +)] +#![deny( + clippy::all, + clippy::cast_lossless, + clippy::redundant_closure_for_method_calls, + clippy::unnested_or_patterns, + clippy::trivially_copy_pass_by_ref, + clippy::needless_pass_by_value, + clippy::match_wildcard_for_single_variants, + clippy::map_unwrap_or, + unused_qualifications, + unused_import_braces, + unused_lifetimes, + unreachable_pub, + trivial_numeric_casts, + rustdoc::broken_intra_doc_links, + missing_debug_implementations, + missing_copy_implementations, + deprecated_in_future, + meta_variable_misuse, + non_ascii_idents, + rust_2018_compatibility, + rust_2018_idioms, + future_incompatible, + nonstandard_style, + missing_docs +)] +#![allow( + clippy::module_name_repetitions, + clippy::too_many_lines, + rustdoc::missing_doc_code_examples +)] mod position; mod punctuator; @@ -41,16 +83,26 @@ pub use self::{ /// Represents all the possible symbols searched for by the [`Contains`][contains] operation. /// /// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains -#[derive(Clone, Copy, Debug, PartialEq)] -pub(crate) enum ContainsSymbol { +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum ContainsSymbol { + /// A super property access (`super.prop`). SuperProperty, + /// A super constructor call (`super(args)`). SuperCall, + /// A yield expression (`yield 5`). YieldExpression, + /// An await expression (`await 4`). AwaitExpression, + /// The new target expression (`new.target`). NewTarget, + /// The body of a class definition. ClassBody, + /// The super class of a class definition. ClassHeritage, + /// A this expression (`this`). This, + /// A method definition. MethodDefinition, } @@ -88,43 +140,20 @@ fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize } } -/// This parses the given source code, and then makes sure that -/// the resulting `StatementList` is formatted in the same manner -/// as the source code. This is expected to have a preceding -/// newline. -/// -/// This is a utility function for tests. It was made in case people -/// are using different indents in their source files. This fixes -/// any strings which may have been changed in a different indent -/// level. -#[cfg(test)] -fn test_formatting(source: &'static str) { - use crate::{syntax::Parser, Context}; - - // Remove preceding newline. - let source = &source[1..]; - - // Find out how much the code is indented - let first_line = &source[..source.find('\n').unwrap()]; - let trimmed_first_line = first_line.trim(); - let characters_to_remove = first_line.len() - trimmed_first_line.len(); +/// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice]. +trait ToStringEscaped { + /// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its + /// codepoint value. + fn to_string_escaped(&self) -> String; +} - let scenario = source - .lines() - .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line - .collect::>() - .join("\n"); - let mut context = Context::default(); - let result = Parser::new(scenario.as_bytes()) - .parse_all(&mut context) - .expect("parsing failed") - .to_interned_string(context.interner()); - if scenario != result { - eprint!("========= Expected:\n{scenario}"); - eprint!("========= Got:\n{result}"); - // Might be helpful to find differing whitespace - eprintln!("========= Expected: {scenario:?}"); - eprintln!("========= Got: {result:?}"); - panic!("parsing test did not give the correct result (see above)"); +impl ToStringEscaped for [u16] { + fn to_string_escaped(&self) -> String { + char::decode_utf16(self.iter().copied()) + .map(|r| match r { + Ok(c) => String::from(c), + Err(e) => format!("\\u{:04X}", e.unpaired_surrogate()), + }) + .collect() } } diff --git a/boa_engine/src/syntax/ast/pattern.rs b/boa_ast/src/pattern.rs similarity index 97% rename from boa_engine/src/syntax/ast/pattern.rs rename to boa_ast/src/pattern.rs index 080c8697f3..852ea1a0bf 100644 --- a/boa_engine/src/syntax/ast/pattern.rs +++ b/boa_ast/src/pattern.rs @@ -22,8 +22,8 @@ //! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern //! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; @@ -36,7 +36,7 @@ use super::{ /// An object or array pattern binding or assignment. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Pattern { /// An object pattern (`let {a, b, c} = object`). @@ -82,6 +82,7 @@ impl Pattern { /// /// A single pattern may have 0 to n identifiers. #[inline] + #[must_use] pub fn idents(&self) -> Vec { match &self { Pattern::Object(pattern) => pattern.idents(), @@ -149,7 +150,7 @@ impl VisitWith for Pattern { /// /// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern /// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ObjectPattern(Box<[ObjectPatternElement]>); @@ -183,19 +184,32 @@ impl ToInternedString for ObjectPattern { impl ObjectPattern { /// Creates a new object binding pattern. #[inline] - pub(in crate::syntax) fn new(bindings: Box<[ObjectPatternElement]>) -> Self { + #[must_use] + pub fn new(bindings: Box<[ObjectPatternElement]>) -> Self { Self(bindings) } /// Gets the bindings for the object binding pattern. #[inline] - pub(crate) fn bindings(&self) -> &[ObjectPatternElement] { + #[must_use] + pub fn bindings(&self) -> &[ObjectPatternElement] { &self.0 } - // Returns true if the object binding pattern has a rest element. + /// Gets the list of identifiers declared by the object binding pattern. + #[inline] + #[must_use] + pub fn idents(&self) -> Vec { + self.0 + .iter() + .flat_map(ObjectPatternElement::idents) + .collect() + } + + /// Returns true if the object binding pattern has a rest element. #[inline] - pub(crate) fn has_rest(&self) -> bool { + #[must_use] + pub fn has_rest(&self) -> bool { matches!( self.0.last(), Some(ObjectPatternElement::RestProperty { .. }) @@ -211,15 +225,6 @@ impl ObjectPattern { pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { self.0.iter().any(|e| e.contains(symbol)) } - - /// Gets the list of identifiers declared by the object binding pattern. - #[inline] - pub(crate) fn idents(&self) -> Vec { - self.0 - .iter() - .flat_map(ObjectPatternElement::idents) - .collect() - } } impl VisitWith for ObjectPattern { @@ -253,7 +258,7 @@ impl VisitWith for ObjectPattern { /// /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern /// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ArrayPattern(Box<[ArrayPatternElement]>); @@ -286,25 +291,18 @@ impl ToInternedString for ArrayPattern { impl ArrayPattern { /// Creates a new array binding pattern. #[inline] - pub(in crate::syntax) fn new(bindings: Box<[ArrayPatternElement]>) -> Self { + #[must_use] + pub fn new(bindings: Box<[ArrayPatternElement]>) -> Self { Self(bindings) } /// Gets the bindings for the array binding pattern. #[inline] - pub(crate) fn bindings(&self) -> &[ArrayPatternElement] { + #[must_use] + pub fn bindings(&self) -> &[ArrayPatternElement] { &self.0 } - #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - self.0.iter().any(ArrayPatternElement::contains_arguments) - } - - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.0.iter().any(|e| e.contains(symbol)) - } - /// Gets the list of identifiers declared by the array binding pattern. #[inline] pub(crate) fn idents(&self) -> Vec { @@ -313,6 +311,15 @@ impl ArrayPattern { .flat_map(ArrayPatternElement::idents) .collect() } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.0.iter().any(ArrayPatternElement::contains_arguments) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.0.iter().any(|e| e.contains(symbol)) + } } impl VisitWith for ArrayPattern { @@ -343,7 +350,7 @@ impl VisitWith for ArrayPattern { /// /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty /// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum ObjectPatternElement { /// SingleName represents one of the following properties: @@ -752,7 +759,7 @@ impl VisitWith for ObjectPatternElement { /// /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement /// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum ArrayPatternElement { /// Elision represents the elision of an item in the array binding pattern. diff --git a/boa_engine/src/syntax/ast/position.rs b/boa_ast/src/position.rs similarity index 95% rename from boa_engine/src/syntax/ast/position.rs rename to boa_ast/src/position.rs index c133b132ad..dafba2fb30 100644 --- a/boa_engine/src/syntax/ast/position.rs +++ b/boa_ast/src/position.rs @@ -6,7 +6,7 @@ use std::{cmp::Ordering, fmt, num::NonZeroU32}; /// /// ## Similar Implementations /// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Position { /// Line number. @@ -19,6 +19,7 @@ impl Position { /// Creates a new `Position`. #[inline] #[track_caller] + #[must_use] pub fn new(line_number: u32, column_number: u32) -> Self { Self { line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"), @@ -28,12 +29,14 @@ impl Position { /// Gets the line number of the position. #[inline] + #[must_use] pub fn line_number(self) -> u32 { self.line_number.get() } /// Gets the column number of the position. #[inline] + #[must_use] pub fn column_number(self) -> u32 { self.column_number.get() } @@ -51,7 +54,7 @@ impl fmt::Display for Position { /// /// Note that spans are of the form [start, end) i.e. that the start position is inclusive /// and the end position is exclusive. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Span { start: Position, @@ -60,8 +63,13 @@ pub struct Span { impl Span { /// Creates a new `Span`. + /// + /// # Panics + /// + /// Panics if the start position is bigger than the end position. #[inline] #[track_caller] + #[must_use] pub fn new(start: Position, end: Position) -> Self { assert!(start <= end, "a span cannot start after its end"); @@ -70,12 +78,14 @@ impl Span { /// Gets the starting position of the span. #[inline] + #[must_use] pub fn start(self) -> Position { self.start } /// Gets the final position of the span. #[inline] + #[must_use] pub fn end(self) -> Position { self.end } @@ -121,8 +131,9 @@ impl fmt::Display for Span { } #[cfg(test)] -#[allow(clippy::similar_names)] mod tests { + #![allow(clippy::similar_names)] + #![allow(unused_must_use)] use super::{Position, Span}; /// Checks that we cannot create a position with 0 as the column. diff --git a/boa_engine/src/syntax/ast/property.rs b/boa_ast/src/property.rs similarity index 95% rename from boa_engine/src/syntax/ast/property.rs rename to boa_ast/src/property.rs index 6a465dbb63..72abc19ffc 100644 --- a/boa_engine/src/syntax/ast/property.rs +++ b/boa_ast/src/property.rs @@ -1,7 +1,7 @@ //! Property definition related types, used in object literals and class definitions. -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; @@ -24,7 +24,7 @@ use super::{ /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript // TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum PropertyDefinition { /// Puts a variable into an object. @@ -173,7 +173,7 @@ impl VisitWith for PropertyDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum MethodDefinition { /// The `get` syntax binds an object property to a function that will be called when that property is looked up. @@ -249,8 +249,9 @@ pub enum MethodDefinition { } impl MethodDefinition { - /// Return the body of the method. - pub(crate) fn body(&self) -> &StatementList { + /// Gets the body of the method. + #[must_use] + pub fn body(&self) -> &StatementList { match self { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) @@ -261,8 +262,9 @@ impl MethodDefinition { } } - /// Return the parameters of the method. - pub(crate) fn parameters(&self) -> &FormalParameterList { + /// Gets the parameters of the method. + #[must_use] + pub fn parameters(&self) -> &FormalParameterList { match self { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) @@ -310,7 +312,7 @@ impl VisitWith for MethodDefinition { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyName -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum PropertyName { /// A `Literal` property name can be either an identifier, a string or a numeric literal. @@ -332,7 +334,8 @@ pub enum PropertyName { impl PropertyName { /// Returns the literal property name if it exists. - pub(crate) fn literal(&self) -> Option { + #[must_use] + pub fn literal(&self) -> Option { if let Self::Literal(sym) = self { Some(*sym) } else { @@ -341,7 +344,8 @@ impl PropertyName { } /// Returns the expression if the property name is computed. - pub(crate) fn computed(&self) -> Option<&Expression> { + #[must_use] + pub fn computed(&self) -> Option<&Expression> { if let Self::Computed(expr) = self { Some(expr) } else { @@ -350,7 +354,8 @@ impl PropertyName { } /// Returns either the literal property name or the computed const string property name. - pub(in crate::syntax) fn prop_name(&self) -> Option { + #[must_use] + pub fn prop_name(&self) -> Option { match self { PropertyName::Literal(sym) | PropertyName::Computed(Expression::Literal(Literal::String(sym))) => Some(*sym), @@ -424,9 +429,11 @@ impl VisitWith for PropertyName { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-ClassElementName -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub(crate) enum ClassElementName { +pub enum ClassElementName { + /// A public property. PropertyName(PropertyName), + /// A private property. PrivateIdentifier(Sym), } diff --git a/boa_engine/src/syntax/ast/punctuator.rs b/boa_ast/src/punctuator.rs similarity index 98% rename from boa_engine/src/syntax/ast/punctuator.rs rename to boa_ast/src/punctuator.rs index 0f678d9948..3ab065dc6b 100644 --- a/boa_engine/src/syntax/ast/punctuator.rs +++ b/boa_ast/src/punctuator.rs @@ -5,7 +5,7 @@ //! //! [spec]: https://tc39.es/ecma262/#prod-Punctuator -use crate::syntax::ast::expression::operator::{ +use crate::expression::operator::{ assign::AssignOp, binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, }; @@ -20,7 +20,7 @@ use std::{ /// - [ECMAScript Reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-Punctuator -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum Punctuator { /// `+` @@ -143,6 +143,7 @@ impl Punctuator { /// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator /// /// If there is no match, `None` will be returned. + #[must_use] pub const fn as_assign_op(self) -> Option { match self { Self::Assign => Some(AssignOp::Assign), @@ -168,6 +169,7 @@ impl Punctuator { /// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator /// /// If there is no match, `None` will be returned. + #[must_use] pub const fn as_binary_op(self) -> Option { match self { Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)), @@ -198,6 +200,7 @@ impl Punctuator { } /// Retrieves the punctuator as a static string. + #[must_use] pub const fn as_str(self) -> &'static str { match self { Self::Add => "+", diff --git a/boa_engine/src/syntax/ast/statement/block.rs b/boa_ast/src/statement/block.rs similarity index 71% rename from boa_engine/src/syntax/ast/statement/block.rs rename to boa_ast/src/statement/block.rs index c6a8d61ec4..2e79cec659 100644 --- a/boa_engine/src/syntax/ast/statement/block.rs +++ b/boa_ast/src/statement/block.rs @@ -1,8 +1,8 @@ //! Block AST node. use super::Statement; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Identifier, ContainsSymbol, StatementList}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Identifier, ContainsSymbol, StatementList}; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; @@ -21,23 +21,25 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-BlockStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Default)] pub struct Block { - #[cfg_attr(feature = "deser", serde(flatten))] + #[cfg_attr(feature = "serde", serde(flatten))] statements: StatementList, } impl Block { /// Gets the list of statements and declarations in this block. #[inline] - pub(crate) fn statement_list(&self) -> &StatementList { + #[must_use] + pub fn statement_list(&self) -> &StatementList { &self.statements } /// Get the lexically declared names of the block. #[inline] - pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { + #[must_use] + pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { self.statements.lexically_declared_names() } @@ -97,29 +99,3 @@ impl VisitWith for Block { visitor.visit_statement_list_mut(&mut self.statements) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - { - let a = function_call(); - console.log("hello"); - } - another_statement(); - "#, - ); - // TODO: Once block labels are implemtned, this should be tested: - // super::super::test_formatting( - // r#" - // block_name: { - // let a = function_call(); - // console.log("hello"); - // } - // another_statement(); - // "#, - // ); - } -} diff --git a/boa_engine/src/syntax/ast/statement/if.rs b/boa_ast/src/statement/if.rs similarity index 89% rename from boa_engine/src/syntax/ast/statement/if.rs rename to boa_ast/src/statement/if.rs index adca66a855..25b6cf2c64 100644 --- a/boa_engine/src/syntax/ast/statement/if.rs +++ b/boa_ast/src/statement/if.rs @@ -1,8 +1,8 @@ //! If statement -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -22,7 +22,7 @@ use core::ops::ControlFlow; /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct If { condition: Expression, @@ -33,12 +33,14 @@ pub struct If { impl If { /// Gets the condition of the if statement. #[inline] + #[must_use] pub fn cond(&self) -> &Expression { &self.condition } /// Gets the body to execute if the condition is true. #[inline] + #[must_use] pub fn body(&self) -> &Statement { &self.body } @@ -50,6 +52,7 @@ impl If { } /// Creates an `If` AST node. + #[must_use] pub fn new(condition: Expression, body: Statement, else_node: Option) -> Self { Self { condition, @@ -123,20 +126,3 @@ impl VisitWith for If { ControlFlow::Continue(()) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let a = true ? 5 : 6; - if (false) { - a = 10; - } else { - a = 20; - } - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/statement/iteration/break.rs b/boa_ast/src/statement/iteration/break.rs similarity index 71% rename from boa_engine/src/syntax/ast/statement/iteration/break.rs rename to boa_ast/src/statement/iteration/break.rs index b05c47aeb1..af97bf503d 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/break.rs +++ b/boa_ast/src/statement/iteration/break.rs @@ -1,8 +1,8 @@ use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::Statement; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::Statement; /// The `break` statement terminates the current loop, switch, or label statement and transfers /// program control to the statement following the terminated statement. @@ -18,7 +18,7 @@ use crate::syntax::ast::Statement; /// /// [spec]: https://tc39.es/ecma262/#prod-BreakStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Break { label: Option, @@ -26,11 +26,13 @@ pub struct Break { impl Break { /// Creates a `Break` AST node. + #[must_use] pub fn new(label: Option) -> Self { Self { label } } /// Gets the label of the break statement, if any. + #[must_use] pub fn label(&self) -> Option { self.label } @@ -80,37 +82,3 @@ impl VisitWith for Break { } } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - // Blocks do not store their label, so we cannot test with - // the outer block having a label. - // - // TODO: Once block labels are implemented, this test should - // include them: - // - // ``` - // outer: { - // while (true) { - // break outer; - // } - // skipped_call(); - // } - // ``` - crate::syntax::ast::test_formatting( - r#" - { - while (true) { - break outer; - } - skipped_call(); - } - while (true) { - break; - } - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/statement/iteration/continue.rs b/boa_ast/src/statement/iteration/continue.rs similarity index 92% rename from boa_engine/src/syntax/ast/statement/iteration/continue.rs rename to boa_ast/src/statement/iteration/continue.rs index 7c7f1f1f17..5b46136522 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/continue.rs +++ b/boa_ast/src/statement/iteration/continue.rs @@ -1,5 +1,5 @@ -use crate::syntax::ast::statement::Statement; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::statement::Statement; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; use core::ops::ControlFlow; @@ -16,7 +16,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Continue { label: Option, @@ -24,11 +24,13 @@ pub struct Continue { impl Continue { /// Creates a `Continue` AST node. + #[must_use] pub fn new(label: Option) -> Self { Self { label } } /// Gets the label of this `Continue` statement. + #[must_use] pub fn label(&self) -> Option { self.label } diff --git a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs b/boa_ast/src/statement/iteration/do_while_loop.rs similarity index 91% rename from boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs rename to boa_ast/src/statement/iteration/do_while_loop.rs index 5ebd5de0b1..0be9eb6ad2 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs +++ b/boa_ast/src/statement/iteration/do_while_loop.rs @@ -1,6 +1,6 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -16,7 +16,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#sec-do-while-statement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct DoWhileLoop { body: Box, @@ -26,17 +26,20 @@ pub struct DoWhileLoop { impl DoWhileLoop { /// Gets the body of the do-while loop. #[inline] + #[must_use] pub fn body(&self) -> &Statement { &self.body } /// Gets the condition of the do-while loop. #[inline] + #[must_use] pub fn cond(&self) -> &Expression { &self.condition } /// Creates a `DoWhileLoop` AST node. #[inline] + #[must_use] pub fn new(body: Statement, condition: Expression) -> Self { Self { body: body.into(), diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs b/boa_ast/src/statement/iteration/for_in_loop.rs similarity index 94% rename from boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs rename to boa_ast/src/statement/iteration/for_in_loop.rs index 0b04974dae..341dd10ded 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs +++ b/boa_ast/src/statement/iteration/for_in_loop.rs @@ -1,10 +1,10 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ expression::Expression, statement::{iteration::IterableLoopInitializer, Statement}, ContainsSymbol, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -15,7 +15,7 @@ use core::ops::ControlFlow; /// /// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in /// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ForInLoop { initializer: IterableLoopInitializer, @@ -26,6 +26,7 @@ pub struct ForInLoop { impl ForInLoop { /// Creates a new `ForInLoop`. #[inline] + #[must_use] pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self { Self { initializer, @@ -36,18 +37,21 @@ impl ForInLoop { /// Gets the initializer of the for...in loop. #[inline] + #[must_use] pub fn initializer(&self) -> &IterableLoopInitializer { &self.initializer } /// Gets the target object of the for...in loop. #[inline] + #[must_use] pub fn target(&self) -> &Expression { &self.target } /// Gets the body of the for...in loop. #[inline] + #[must_use] pub fn body(&self) -> &Statement { &self.body } diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs b/boa_ast/src/statement/iteration/for_loop.rs similarity index 95% rename from boa_engine/src/syntax/ast/statement/iteration/for_loop.rs rename to boa_ast/src/statement/iteration/for_loop.rs index c697e2c7e3..e12753ff19 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs +++ b/boa_ast/src/statement/iteration/for_loop.rs @@ -1,11 +1,11 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ declaration::{LexicalDeclaration, VarDeclaration, Variable}, expression::Identifier, statement::Statement, ContainsSymbol, Expression, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -19,17 +19,18 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ForLoop { - #[cfg_attr(feature = "deser", serde(flatten))] + #[cfg_attr(feature = "serde", serde(flatten))] inner: Box, } impl ForLoop { /// Creates a new for loop AST node. #[inline] - pub(in crate::syntax) fn new( + #[must_use] + pub fn new( init: Option, condition: Option, final_expr: Option, @@ -42,24 +43,28 @@ impl ForLoop { /// Gets the initialization node. #[inline] + #[must_use] pub fn init(&self) -> Option<&ForLoopInitializer> { self.inner.init() } /// Gets the loop condition node. #[inline] + #[must_use] pub fn condition(&self) -> Option<&Expression> { self.inner.condition() } /// Gets the final expression node. #[inline] + #[must_use] pub fn final_expr(&self) -> Option<&Expression> { self.inner.final_expr() } /// Gets the body of the for loop. #[inline] + #[must_use] pub fn body(&self) -> &Statement { self.inner.body() } @@ -148,7 +153,7 @@ impl VisitWith for ForLoop { } /// Inner structure to avoid multiple indirections in the heap. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] struct InnerForLoop { init: Option, @@ -207,7 +212,7 @@ impl InnerForLoop { /// declarations instead of only one. /// /// [spec]: https://tc39.es/ecma262/#prod-ForStatement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum ForLoopInitializer { /// An expression initializer. @@ -225,7 +230,8 @@ impl ForLoopInitializer { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames - pub(crate) fn bound_names(&self) -> Vec { + #[must_use] + pub fn bound_names(&self) -> Vec { match self { ForLoopInitializer::Lexical(lex) => lex .variable_list() diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs b/boa_ast/src/statement/iteration/for_of_loop.rs similarity index 93% rename from boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs rename to boa_ast/src/statement/iteration/for_of_loop.rs index cfcd1079c3..538cad9df7 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs +++ b/boa_ast/src/statement/iteration/for_of_loop.rs @@ -1,10 +1,10 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ expression::Expression, statement::{iteration::IterableLoopInitializer, Statement}, ContainsSymbol, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -20,7 +20,7 @@ use core::ops::ControlFlow; /// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of /// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement /// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ForOfLoop { init: IterableLoopInitializer, @@ -32,6 +32,7 @@ pub struct ForOfLoop { impl ForOfLoop { /// Creates a new "for of" loop AST node. #[inline] + #[must_use] pub fn new( init: IterableLoopInitializer, iterable: Expression, @@ -48,25 +49,29 @@ impl ForOfLoop { /// Gets the initializer of the for...of loop. #[inline] + #[must_use] pub fn init(&self) -> &IterableLoopInitializer { &self.init } /// Gets the iterable expression of the for...of loop. #[inline] + #[must_use] pub fn iterable(&self) -> &Expression { &self.iterable } /// Gets the body to execute in the for...of loop. #[inline] + #[must_use] pub fn body(&self) -> &Statement { &self.body } /// Returns true if this "for...of" loop is an "for await...of" loop. #[inline] - pub(crate) fn r#await(&self) -> bool { + #[must_use] + pub fn r#await(&self) -> bool { self.r#await } diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_ast/src/statement/iteration/mod.rs similarity index 94% rename from boa_engine/src/syntax/ast/statement/iteration/mod.rs rename to boa_ast/src/statement/iteration/mod.rs index ff1ecd3037..5ce33c7708 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/mod.rs +++ b/boa_ast/src/statement/iteration/mod.rs @@ -8,7 +8,7 @@ mod for_loop; mod for_of_loop; mod while_loop; -use crate::syntax::ast::{ +use crate::{ declaration::Binding, expression::{access::PropertyAccess, Identifier}, pattern::Pattern, @@ -24,14 +24,11 @@ pub use self::{ r#continue::Continue, while_loop::WhileLoop, }; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; use super::ContainsSymbol; -#[cfg(test)] -mod tests; - /// A `for-in`, `for-of` and `for-await-of` loop initializer. /// /// The [spec] specifies only single bindings for the listed types of loops, which makes us @@ -39,7 +36,7 @@ mod tests; /// can have more than one binding. /// /// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum IterableLoopInitializer { /// An already declared variable. @@ -65,7 +62,8 @@ impl IterableLoopInitializer { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames - pub(crate) fn bound_names(&self) -> Vec { + #[must_use] + pub fn bound_names(&self) -> Vec { match self { Self::Let(binding) | Self::Const(binding) => binding.idents(), _ => Vec::new(), diff --git a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs b/boa_ast/src/statement/iteration/while_loop.rs similarity index 91% rename from boa_engine/src/syntax/ast/statement/iteration/while_loop.rs rename to boa_ast/src/statement/iteration/while_loop.rs index 6b2eeb396c..dc010fb270 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs +++ b/boa_ast/src/statement/iteration/while_loop.rs @@ -1,6 +1,6 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -15,7 +15,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct WhileLoop { condition: Expression, @@ -25,6 +25,7 @@ pub struct WhileLoop { impl WhileLoop { /// Creates a `WhileLoop` AST node. #[inline] + #[must_use] pub fn new(condition: Expression, body: Statement) -> Self { Self { condition, @@ -34,12 +35,14 @@ impl WhileLoop { /// Gets the condition of the while loop. #[inline] + #[must_use] pub fn condition(&self) -> &Expression { &self.condition } /// Gets the body of the while loop. #[inline] + #[must_use] pub fn body(&self) -> &Statement { &self.body } diff --git a/boa_engine/src/syntax/ast/statement/labelled.rs b/boa_ast/src/statement/labelled.rs similarity index 94% rename from boa_engine/src/syntax/ast/statement/labelled.rs rename to boa_ast/src/statement/labelled.rs index 477fdd0af0..25aefff37a 100644 --- a/boa_engine/src/syntax/ast/statement/labelled.rs +++ b/boa_ast/src/statement/labelled.rs @@ -1,7 +1,7 @@ use super::Statement; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{function::Function, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{function::Function, ContainsSymbol}; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; @@ -14,7 +14,7 @@ use core::ops::ControlFlow; /// /// [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))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum LabelledItem { /// A labelled [`Function`]. @@ -94,7 +94,7 @@ impl VisitWith for LabelledItem { /// See [`LabelledItem`] for more information. /// /// [spec]: https://tc39.es/ecma262/#sec-labelled-statements -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Labelled { item: Box, @@ -103,6 +103,7 @@ pub struct Labelled { impl Labelled { /// Creates a new `Labelled` statement. + #[must_use] pub fn new(item: LabelledItem, label: Sym) -> Self { Self { item: Box::new(item), @@ -111,11 +112,13 @@ impl Labelled { } /// Gets the labelled item. + #[must_use] pub fn item(&self) -> &LabelledItem { &self.item } /// Gets the label name. + #[must_use] pub fn label(&self) -> Sym { self.label } diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_ast/src/statement/mod.rs similarity index 95% rename from boa_engine/src/syntax/ast/statement/mod.rs rename to boa_ast/src/statement/mod.rs index 831d395651..c3d9c56299 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/boa_ast/src/statement/mod.rs @@ -24,16 +24,15 @@ pub use self::{ labelled::{Labelled, LabelledItem}, r#if::If, r#return::Return, - r#try::{Catch, Finally, Try}, + r#try::{Catch, ErrorHandler, Finally, Try}, switch::{Case, Switch}, throw::Throw, }; use core::ops::ControlFlow; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use rustc_hash::FxHashSet; -use tap::Tap; use super::{ declaration::{Binding, VarDeclaration}, @@ -44,7 +43,7 @@ use super::{ /// The `Statement` Parse Node. /// /// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Statement { /// See [`Block`]. @@ -115,7 +114,7 @@ impl Statement { /// This will not prefix the value with any indentation. If you want to prefix this with proper /// indents, use [`to_indented_string()`](Self::to_indented_string). pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { - match self { + let mut s = match self { Self::Block(block) => return block.to_indented_string(interner, indentation), Self::Var(var) => var.to_interned_string(interner), Self::Empty => return ";".to_owned(), @@ -135,11 +134,13 @@ impl Statement { Self::Labelled(labelled) => return labelled.to_interned_string(interner), Self::Throw(throw) => throw.to_interned_string(interner), Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation), - } - .tap_mut(|s| s.push(';')) + }; + s.push(';'); + s } - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + /// Gets the var declared names of this `Statement`. + pub fn var_declared_names(&self, vars: &mut FxHashSet) { match self { Self::Var(VarDeclaration(list)) => { for decl in list.as_ref() { @@ -212,7 +213,7 @@ impl Statement { } } if let Some(finally) = try_statement.finally() { - for node in finally.statement_list().statements() { + for node in finally.block().statement_list().statements() { node.var_declared_names(vars); } } @@ -281,8 +282,14 @@ impl Statement { } } + /// `IsLabelledFunction` static operation, as defined by the [spec]. + /// + /// Returns `true` if this `Statement` is a labelled function. + /// + /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction #[inline] - pub(crate) fn is_labelled_function(&self) -> bool { + #[must_use] + pub fn is_labelled_function(&self) -> bool { match self { Self::Labelled(stmt) => match stmt.item() { LabelledItem::Function(_) => true, diff --git a/boa_engine/src/syntax/ast/statement/return.rs b/boa_ast/src/statement/return.rs similarity index 80% rename from boa_engine/src/syntax/ast/statement/return.rs rename to boa_ast/src/statement/return.rs index 4370f64aec..7640dea294 100644 --- a/boa_engine/src/syntax/ast/statement/return.rs +++ b/boa_ast/src/statement/return.rs @@ -1,5 +1,5 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; @@ -20,7 +20,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Return { target: Option, @@ -28,11 +28,13 @@ pub struct Return { impl Return { /// Gets the target expression value of this `Return` statement. + #[must_use] pub fn target(&self) -> Option<&Expression> { self.target.as_ref() } /// Creates a `Return` AST node. + #[must_use] pub fn new(expression: Option) -> Self { Self { target: expression } } @@ -85,23 +87,3 @@ impl VisitWith for Return { } } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - function say_hello(msg) { - if (msg === "") { - return 0; - } - console.log("hello " + msg); - return; - } - say_hello(""); - say_hello("world"); - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/statement/switch/mod.rs b/boa_ast/src/statement/switch.rs similarity index 94% rename from boa_engine/src/syntax/ast/statement/switch/mod.rs rename to boa_ast/src/statement/switch.rs index 44d85c44d5..de3eeb4f5b 100644 --- a/boa_engine/src/syntax/ast/statement/switch/mod.rs +++ b/boa_ast/src/statement/switch.rs @@ -1,16 +1,13 @@ //! Switch node. //! -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, statement::Statement, StatementList}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Expression, statement::Statement, StatementList}; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; use super::ContainsSymbol; -#[cfg(test)] -mod tests; - /// A case clause inside a [`Switch`] statement, as defined by the [spec]. /// /// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical @@ -19,7 +16,7 @@ mod tests; /// /// [spec]: https://tc39.es/ecma262/#prod-CaseClause /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Case { condition: Expression, @@ -29,18 +26,21 @@ pub struct Case { impl Case { /// Creates a `Case` AST node. #[inline] + #[must_use] pub fn new(condition: Expression, body: StatementList) -> Self { Self { condition, body } } /// Gets the condition of the case. #[inline] + #[must_use] pub fn condition(&self) -> &Expression { &self.condition } /// Gets the statement listin the body of the case. #[inline] + #[must_use] pub fn body(&self) -> &StatementList { &self.body } @@ -95,7 +95,7 @@ impl VisitWith for Case { /// /// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Switch { val: Expression, @@ -106,6 +106,7 @@ pub struct Switch { impl Switch { /// Creates a `Switch` AST node. #[inline] + #[must_use] pub fn new(val: Expression, cases: Box<[Case]>, default: Option) -> Self { Self { val, @@ -116,18 +117,21 @@ impl Switch { /// Gets the value to switch. #[inline] + #[must_use] pub fn val(&self) -> &Expression { &self.val } /// Gets the list of cases for the switch statement. #[inline] + #[must_use] pub fn cases(&self) -> &[Case] { &self.cases } /// Gets the default statement list, if any. #[inline] + #[must_use] pub fn default(&self) -> Option<&StatementList> { self.default.as_ref() } diff --git a/boa_engine/src/syntax/ast/statement/throw.rs b/boa_ast/src/statement/throw.rs similarity index 81% rename from boa_engine/src/syntax/ast/statement/throw.rs rename to boa_ast/src/statement/throw.rs index c1747c6f51..f3d6dac42c 100644 --- a/boa_engine/src/syntax/ast/statement/throw.rs +++ b/boa_ast/src/statement/throw.rs @@ -1,5 +1,5 @@ -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{statement::Statement, ContainsSymbol, Expression}; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{statement::Statement, ContainsSymbol, Expression}; use boa_interner::{Interner, ToInternedString}; use core::ops::ControlFlow; @@ -17,7 +17,7 @@ use core::ops::ControlFlow; /// /// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Throw { target: Expression, @@ -25,11 +25,13 @@ pub struct Throw { impl Throw { /// Gets the target expression of this `Throw` statement. + #[must_use] pub fn target(&self) -> &Expression { &self.target } /// Creates a `Throw` AST node. + #[must_use] pub fn new(target: Expression) -> Self { Self { target } } @@ -72,19 +74,3 @@ impl VisitWith for Throw { visitor.visit_expression_mut(&mut self.target) } } - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - try { - throw "hello"; - } catch(e) { - console.log(e); - } - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/statement/try/mod.rs b/boa_ast/src/statement/try.rs similarity index 74% rename from boa_engine/src/syntax/ast/statement/try/mod.rs rename to boa_ast/src/statement/try.rs index 0237162f85..bed249076b 100644 --- a/boa_engine/src/syntax/ast/statement/try/mod.rs +++ b/boa_ast/src/statement/try.rs @@ -1,20 +1,17 @@ //! Error handling statements -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ +use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{ declaration::Binding, statement::{Block, Statement}, StatementListItem, }; -use crate::try_break; use boa_interner::{Interner, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; use super::ContainsSymbol; -#[cfg(test)] -mod tests; - /// The `try...catch` statement marks a block of statements to try and specifies a response /// should an exception be thrown. /// @@ -28,64 +25,72 @@ mod tests; /// /// [spec]: https://tc39.es/ecma262/#prod-TryStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Try { block: Block, - catch: Option, - finally: Option, + handler: ErrorHandler, +} + +/// The type of error handler in a [`Try`] statement. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum ErrorHandler { + /// A [`Catch`] error handler. + Catch(Catch), + /// A [`Finally`] error handler. + Finally(Finally), + /// A [`Catch`] and [`Finally`] error handler. + Full(Catch, Finally), } impl Try { /// Creates a new `Try` AST node. #[inline] - pub(in crate::syntax) fn new( - block: Block, - catch: Option, - finally: Option, - ) -> Self { - assert!( - catch.is_some() || finally.is_some(), - "one of catch or finally must be pressent" - ); - - Self { - block, - catch, - finally, - } + #[must_use] + pub fn new(block: Block, handler: ErrorHandler) -> Self { + Self { block, handler } } /// Gets the `try` block. #[inline] + #[must_use] pub fn block(&self) -> &Block { &self.block } /// Gets the `catch` block, if any. #[inline] + #[must_use] pub fn catch(&self) -> Option<&Catch> { - self.catch.as_ref() + match &self.handler { + ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c), + ErrorHandler::Finally(_) => None, + } } /// Gets the `finally` block, if any. #[inline] - pub fn finally(&self) -> Option<&Block> { - self.finally.as_ref().map(Finally::block) + #[must_use] + pub fn finally(&self) -> Option<&Finally> { + match &self.handler { + ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f), + ErrorHandler::Catch(_) => None, + } } #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.block.contains_arguments() - || matches!(self.catch, Some(ref catch) if catch.contains_arguments()) - || matches!(self.finally, Some(ref finally) if finally.contains_arguments()) + || matches!(self.catch(), Some(catch) if catch.contains_arguments()) + || matches!(self.finally(), Some(finally) if finally.contains_arguments()) } #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { self.block.contains(symbol) - || matches!(self.catch, Some(ref catch) if catch.contains(symbol)) - || matches!(self.finally, Some(ref finally) if finally.contains(symbol)) + || matches!(self.catch(), Some(catch) if catch.contains(symbol)) + || matches!(self.finally(), Some(finally) if finally.contains(symbol)) } } @@ -97,11 +102,11 @@ impl ToIndentedString for Try { self.block.to_indented_string(interner, indentation) ); - if let Some(ref catch) = self.catch { + if let Some(catch) = self.catch() { buf.push_str(&catch.to_indented_string(interner, indentation)); } - if let Some(ref finally) = self.finally { + if let Some(finally) = self.finally() { buf.push_str(&finally.to_indented_string(interner, indentation)); } buf @@ -121,10 +126,10 @@ impl VisitWith for Try { V: Visitor<'a>, { try_break!(visitor.visit_block(&self.block)); - if let Some(catch) = &self.catch { + if let Some(catch) = &self.catch() { try_break!(visitor.visit_catch(catch)); } - if let Some(finally) = &self.finally { + if let Some(finally) = &self.finally() { try_break!(visitor.visit_finally(finally)); } ControlFlow::Continue(()) @@ -135,18 +140,20 @@ impl VisitWith for Try { V: VisitorMut<'a>, { try_break!(visitor.visit_block_mut(&mut self.block)); - if let Some(catch) = &mut self.catch { - try_break!(visitor.visit_catch_mut(catch)); - } - if let Some(finally) = &mut self.finally { - try_break!(visitor.visit_finally_mut(finally)); + match &mut self.handler { + ErrorHandler::Catch(c) => try_break!(visitor.visit_catch_mut(c)), + ErrorHandler::Finally(f) => try_break!(visitor.visit_finally_mut(f)), + ErrorHandler::Full(c, f) => { + try_break!(visitor.visit_catch_mut(c)); + try_break!(visitor.visit_finally_mut(f)); + } } ControlFlow::Continue(()) } } /// Catch block. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Catch { parameter: Option, @@ -156,18 +163,21 @@ pub struct Catch { impl Catch { /// Creates a new catch block. #[inline] - pub(in crate::syntax) fn new(parameter: Option, block: Block) -> Self { + #[must_use] + pub fn new(parameter: Option, block: Block) -> Self { Self { parameter, block } } /// Gets the parameter of the catch block. #[inline] + #[must_use] pub fn parameter(&self) -> Option<&Binding> { self.parameter.as_ref() } /// Retrieves the catch execution block. #[inline] + #[must_use] pub fn block(&self) -> &Block { &self.block } @@ -229,7 +239,7 @@ impl VisitWith for Catch { } /// Finally block. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Finally { block: Block, @@ -238,6 +248,7 @@ pub struct Finally { impl Finally { /// Gets the finally block. #[inline] + #[must_use] pub fn block(&self) -> &Block { &self.block } diff --git a/boa_engine/src/syntax/ast/statement_list/mod.rs b/boa_ast/src/statement_list.rs similarity index 88% rename from boa_engine/src/syntax/ast/statement_list/mod.rs rename to boa_ast/src/statement_list.rs index c43bc3bd8c..7f141974d9 100644 --- a/boa_engine/src/syntax/ast/statement_list/mod.rs +++ b/boa_ast/src/statement_list.rs @@ -1,24 +1,21 @@ //! Statement list node. use super::{declaration::Binding, Declaration}; -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Identifier, statement::Statement, ContainsSymbol}; use crate::try_break; +use crate::visitor::{VisitWith, Visitor, VisitorMut}; +use crate::{expression::Identifier, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; use rustc_hash::FxHashSet; use std::cmp::Ordering; -#[cfg(test)] -mod tests; - /// An item inside a [`StatementList`] Parse Node, as defined by the [spec]. /// /// Items in a `StatementList` can be either [`Declaration`]s (functions, classes, let/const declarations) /// or [`Statement`]s (if, while, var statement). /// /// [spec]: https://tc39.es/ecma262/#prod-StatementListItem -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum StatementListItem { /// See [`Statement`]. @@ -29,7 +26,8 @@ pub enum StatementListItem { impl StatementListItem { /// Returns a node ordering based on the hoistability of each statement. - pub(crate) fn hoistable_order(a: &Self, b: &Self) -> Ordering { + #[must_use] + pub fn hoistable_order(a: &Self, b: &Self) -> Ordering { match (a, b) { ( Self::Declaration(Declaration::Function(_)), @@ -42,8 +40,9 @@ impl StatementListItem { } } + /// Gets the var declared names of this `StatementListItem`. #[inline] - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + pub fn var_declared_names(&self, vars: &mut FxHashSet) { match self { StatementListItem::Statement(stmt) => stmt.var_declared_names(vars), StatementListItem::Declaration(_) => {} @@ -57,7 +56,8 @@ impl StatementListItem { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments #[inline] - pub(crate) fn contains_arguments(&self) -> bool { + #[must_use] + pub fn contains_arguments(&self) -> bool { match self { StatementListItem::Statement(stmt) => stmt.contains_arguments(), StatementListItem::Declaration(decl) => decl.contains_arguments(), @@ -71,7 +71,8 @@ impl StatementListItem { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + #[must_use] + pub fn contains(&self, symbol: ContainsSymbol) -> bool { match self { StatementListItem::Statement(stmt) => stmt.contains(symbol), StatementListItem::Declaration(decl) => decl.contains(symbol), @@ -150,7 +151,7 @@ impl VisitWith for StatementListItem { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-StatementList -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct StatementList { statements: Box<[StatementListItem]>, @@ -160,12 +161,14 @@ pub struct StatementList { impl StatementList { /// Gets the list of statements. #[inline] + #[must_use] pub fn statements(&self) -> &[StatementListItem] { &self.statements } /// Get the strict mode. #[inline] + #[must_use] pub fn strict(&self) -> bool { self.strict } @@ -176,14 +179,15 @@ impl StatementList { self.strict = strict; } + /// Returns the var declared names of a `StatementList`. #[inline] - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + pub fn var_declared_names(&self, vars: &mut FxHashSet) { for stmt in &*self.statements { stmt.var_declared_names(vars); } } - /// Return the lexically declared names of a `StatementList`. + /// Returns the lexically declared names of a `StatementList`. /// /// The returned list may contain duplicates. /// @@ -193,7 +197,8 @@ impl StatementList { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames - pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { + #[must_use] + pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { let mut names = Vec::new(); for node in self.statements() { @@ -216,7 +221,8 @@ impl StatementList { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames - pub(crate) fn lexically_declared_names_top_level(&self) -> Vec { + #[must_use] + pub fn lexically_declared_names_top_level(&self) -> Vec { let mut names = Vec::new(); for node in self.statements() { @@ -254,7 +260,7 @@ impl StatementList { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments #[inline] - pub(crate) fn contains_arguments(&self) -> bool { + pub fn contains_arguments(&self) -> bool { self.statements .iter() .any(StatementListItem::contains_arguments) @@ -267,7 +273,8 @@ impl StatementList { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + #[must_use] + pub fn contains(&self, symbol: ContainsSymbol) -> bool { self.statements.iter().any(|stmt| stmt.contains(symbol)) } } diff --git a/boa_engine/src/syntax/ast/visitor.rs b/boa_ast/src/visitor.rs similarity index 94% rename from boa_engine/src/syntax/ast/visitor.rs rename to boa_ast/src/visitor.rs index ea38a122ee..dd6e8be2be 100644 --- a/boa_engine/src/syntax/ast/visitor.rs +++ b/boa_ast/src/visitor.rs @@ -14,38 +14,38 @@ macro_rules! try_break { }; } -use crate::syntax::ast::declaration::{ +use crate::declaration::{ Binding, Declaration, LexicalDeclaration, VarDeclaration, Variable, VariableList, }; -use crate::syntax::ast::expression::access::{ +use crate::expression::access::{ PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess, SuperPropertyAccess, }; -use crate::syntax::ast::expression::literal::{ +use crate::expression::literal::{ ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral, }; -use crate::syntax::ast::expression::operator::assign::{Assign, AssignTarget}; -use crate::syntax::ast::expression::operator::{Binary, Conditional, Unary}; -use crate::syntax::ast::expression::{ +use crate::expression::operator::assign::{Assign, AssignTarget}; +use crate::expression::operator::{Binary, Conditional, Unary}; +use crate::expression::{ Await, Call, Expression, Identifier, New, Optional, OptionalOperation, OptionalOperationKind, Spread, SuperCall, TaggedTemplate, Yield, }; -use crate::syntax::ast::function::{ +use crate::function::{ ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameter, FormalParameterList, Function, Generator, }; -use crate::syntax::ast::pattern::{ +use crate::pattern::{ ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern, }; -use crate::syntax::ast::property::{MethodDefinition, PropertyDefinition, PropertyName}; -use crate::syntax::ast::statement::iteration::{ +use crate::property::{MethodDefinition, PropertyDefinition, PropertyName}; +use crate::statement::iteration::{ Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop, IterableLoopInitializer, WhileLoop, }; -use crate::syntax::ast::statement::{ +use crate::statement::{ Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw, Try, }; -use crate::syntax::ast::{StatementList, StatementListItem}; +use crate::{StatementList, StatementListItem}; use boa_interner::Sym; /// Creates the default visit function implementation for a particular type @@ -77,7 +77,6 @@ macro_rules! define_visit_mut { /// /// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s /// visitor pattern. -#[allow(unused_variables)] pub trait Visitor<'ast>: Sized { /// Type which will be propagated from the visitor if completing early. type BreakTy; @@ -162,7 +161,6 @@ pub trait Visitor<'ast>: Sized { /// /// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s /// visitor pattern. -#[allow(unused_variables)] pub trait VisitorMut<'ast>: Sized { /// Type which will be propagated from the visitor if completing early. type BreakTy; diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index 5ad745d93e..3d9963d70e 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["deser", "console"] } +boa_ast = { workspace = true, features = ["serde"]} boa_interner.workspace = true rustyline = "10.0.0" rustyline-derive = "0.7.0" diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index 88694009e0..b330225aa4 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -59,7 +59,8 @@ rustdoc::missing_doc_code_examples )] -use boa_engine::{syntax::ast::StatementList, Context}; +use boa_ast::StatementList; +use boa_engine::Context; use clap::{Parser, ValueEnum, ValueHint}; use colored::{Color, Colorize}; use rustyline::{config::Config, error::ReadlineError, EditMode, Editor}; diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index b10e9e9098..b5f443da7e 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -13,7 +13,7 @@ rust-version.workspace = true [features] profiler = ["boa_profiler/profiler"] -deser = ["boa_interner/serde"] +deser = ["boa_interner/serde", "boa_ast/serde"] intl = [ "dep:icu_locale_canonicalizer", "dep:icu_locid", @@ -33,6 +33,7 @@ boa_interner.workspace = true boa_gc.workspace = true boa_profiler.workspace = true boa_macros.workspace = true +boa_ast.workspace = true gc = "0.4.1" serde = { version = "1.0.147", features = ["derive", "rc"] } serde_json = "1.0.87" diff --git a/boa_engine/src/builtins/function/arguments.rs b/boa_engine/src/builtins/function/arguments.rs index 798364c161..4532eaaaa8 100644 --- a/boa_engine/src/builtins/function/arguments.rs +++ b/boa_engine/src/builtins/function/arguments.rs @@ -3,9 +3,9 @@ use crate::{ object::{JsObject, ObjectData}, property::PropertyDescriptor, symbol::{self, WellKnownSymbols}, - syntax::ast::function::FormalParameterList, Context, JsValue, }; +use boa_ast::function::FormalParameterList; use boa_gc::{Finalize, Gc, Trace}; use rustc_hash::FxHashMap; @@ -199,7 +199,7 @@ impl Arguments { let mut bindings = FxHashMap::default(); let mut property_index = 0; - 'outer: for formal in formals.parameters.iter() { + 'outer: for formal in formals.as_ref() { for name in formal.names() { if property_index >= len { break 'outer; diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 3741d63b3d..0236c85e77 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -26,10 +26,11 @@ use crate::{ property::{Attribute, PropertyDescriptor, PropertyKey}, string::utf16, symbol::WellKnownSymbols, - syntax::{ast::function::FormalParameterList, ast::StatementList, Parser}, + syntax::Parser, value::IntegerOrInfinity, Context, JsResult, JsString, JsValue, }; +use boa_ast::{function::FormalParameterList, StatementList}; use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_interner::Sym; use boa_profiler::Profiler; @@ -493,7 +494,7 @@ impl BuiltInFunctionObject { let prototype = get_prototype_from_constructor(new_target, default, context)?; if let Some((body_arg, args)) = args.split_last() { let parameters = if args.is_empty() { - FormalParameterList::empty() + FormalParameterList::default() } else { let mut parameters = Vec::with_capacity(args.len()); for arg in args { @@ -554,7 +555,7 @@ impl BuiltInFunctionObject { // Early Error: If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code, // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". if body.strict() { - for parameter in parameters.parameters.iter() { + for parameter in parameters.as_ref() { for name in parameter.names() { if name == Sym::ARGUMENTS || name == Sym::EVAL { return Err(JsNativeError::syntax() @@ -588,7 +589,7 @@ impl BuiltInFunctionObject { // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors { let lexically_declared_names = body.lexically_declared_names(); - for param in parameters.parameters.as_ref() { + for param in parameters.as_ref() { for param_name in param.names() { if lexically_declared_names .iter() @@ -627,7 +628,7 @@ impl BuiltInFunctionObject { .name(Sym::ANONYMOUS) .generator(true) .compile( - &FormalParameterList::empty(), + &FormalParameterList::default(), &StatementList::default(), context, )?; @@ -640,7 +641,7 @@ impl BuiltInFunctionObject { Ok(function_object) } else { let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile( - &FormalParameterList::empty(), + &FormalParameterList::default(), &StatementList::default(), context, )?; diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index cacf9d02bb..08440efdb2 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -1,10 +1,10 @@ use crate::{ builtins::function::ThisMode, bytecompiler::ByteCompiler, - syntax::ast::{declaration::Binding, function::FormalParameterList, StatementList}, vm::{BindingOpcode, CodeBlock, Opcode}, Context, JsResult, }; +use boa_ast::{declaration::Binding, function::FormalParameterList, StatementList}; use boa_gc::Gc; use boa_interner::Sym; use rustc_hash::FxHashMap; @@ -117,7 +117,7 @@ impl FunctionCompiler { ); } - for parameter in parameters.parameters.iter() { + for parameter in parameters.as_ref() { if parameter.is_rest_param() { compiler.emit_opcode(Opcode::RestParameterInit); } diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 8fdec12531..180040dc1c 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -2,33 +2,33 @@ mod function; use crate::{ environments::{BindingLocator, CompileTimeEnvironment}, - syntax::ast::{ - declaration::{Binding, LexicalDeclaration, VarDeclaration}, - expression::{ - access::{PropertyAccess, PropertyAccessField}, - literal::{self, TemplateElement}, - operator::{ - assign::{AssignOp, AssignTarget}, - binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, - unary::UnaryOp, - }, - Call, Identifier, New, Optional, OptionalOperationKind, - }, - function::{ - ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList, - Function, Generator, - }, - pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, - property::{MethodDefinition, PropertyDefinition, PropertyName}, - statement::{ - iteration::{ForLoopInitializer, IterableLoopInitializer}, - Block, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, LabelledItem, WhileLoop, - }, - Declaration, Expression, Statement, StatementList, StatementListItem, - }, vm::{BindingOpcode, CodeBlock, Opcode}, Context, JsBigInt, JsNativeError, JsResult, JsString, JsValue, }; +use boa_ast::{ + declaration::{Binding, LexicalDeclaration, VarDeclaration}, + expression::{ + access::{PropertyAccess, PropertyAccessField}, + literal::{self, TemplateElement}, + operator::{ + assign::{AssignOp, AssignTarget}, + binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, + unary::UnaryOp, + }, + Call, Identifier, New, Optional, OptionalOperationKind, + }, + function::{ + ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList, + Function, Generator, + }, + pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + statement::{ + iteration::{ForLoopInitializer, IterableLoopInitializer}, + Block, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, LabelledItem, WhileLoop, + }, + Declaration, Expression, Statement, StatementList, StatementListItem, +}; use boa_gc::Gc; use boa_interner::{Interner, Sym}; use rustc_hash::FxHashMap; @@ -1483,13 +1483,13 @@ impl<'b> ByteCompiler<'b> { Expression::Class(class) => self.class(class, true)?, Expression::SuperCall(super_call) => { let contains_spread = super_call - .args() + .arguments() .iter() .any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { self.emit_opcode(Opcode::PushNewArray); - for arg in super_call.args() { + for arg in super_call.arguments() { self.compile_expr(arg, true)?; if let Expression::Spread(_) = arg { self.emit_opcode(Opcode::InitIterator); @@ -1499,7 +1499,7 @@ impl<'b> ByteCompiler<'b> { } } } else { - for arg in super_call.args() { + for arg in super_call.arguments() { self.compile_expr(arg, true)?; } } @@ -1507,7 +1507,7 @@ impl<'b> ByteCompiler<'b> { if contains_spread { self.emit_opcode(Opcode::SuperCallSpread); } else { - self.emit(Opcode::SuperCall, &[super_call.args().len() as u32]); + self.emit(Opcode::SuperCall, &[super_call.arguments().len() as u32]); } if !use_expr { @@ -2589,9 +2589,9 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_decls(finally.statement_list(), configurable_globals); + self.create_decls(finally.block().statement_list(), configurable_globals); self.compile_statement_list( - finally.statement_list(), + finally.block().statement_list(), false, configurable_globals, )?; @@ -3207,7 +3207,7 @@ impl<'b> ByteCompiler<'b> { .context .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), ); - for parameter in expr.parameters().parameters.iter() { + for parameter in expr.parameters().as_ref() { if parameter.is_rest_param() { compiler.emit_opcode(Opcode::RestParameterInit); } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 0a4d592cf2..f2c97fb751 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -20,11 +20,12 @@ use crate::{ object::{FunctionBuilder, GlobalPropertyMap, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - syntax::{ast::StatementList, parser::ParseError, Parser}, + syntax::{parser::ParseError, Parser}, vm::{CallFrame, CodeBlock, FinallyReturn, GeneratorResumeKind, Vm}, JsResult, JsString, JsValue, }; +use boa_ast::StatementList; use boa_gc::Gc; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 1b30759b2b..79e30e1acc 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -1,7 +1,7 @@ use crate::{ - environments::runtime::BindingLocator, property::PropertyDescriptor, - syntax::ast::expression::Identifier, Context, JsString, JsValue, + environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue, }; +use boa_ast::expression::Identifier; use boa_gc::{Cell, Finalize, Gc, Trace}; use rustc_hash::FxHashMap; diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index b1a6020254..7d2f956941 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -1,11 +1,11 @@ use std::cell::Cell; use crate::{ - environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, - syntax::ast::expression::Identifier, Context, JsValue, + environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue, }; use boa_gc::{Cell as GcCell, Finalize, Gc, Trace}; +use boa_ast::expression::Identifier; use rustc_hash::FxHashSet; /// A declarative environment holds binding values at runtime. diff --git a/boa_engine/src/syntax/ast/expression/literal/array.rs b/boa_engine/src/syntax/ast/expression/literal/array.rs deleted file mode 100644 index b072b72a40..0000000000 --- a/boa_engine/src/syntax/ast/expression/literal/array.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Array declaration Expression. - -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{expression::Expression, ContainsSymbol}; -use crate::try_break; -use boa_interner::{Interner, ToInternedString}; -use core::ops::ControlFlow; - -/// An array is an ordered collection of data (either primitive or object depending upon the -/// language). -/// -/// Arrays are used to store multiple values in a single variable. -/// This is compared to a variable that can store only one value. -/// -/// Each item in an array has a number attached to it, called a numeric index, that allows you -/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various -/// methods. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct ArrayLiteral { - arr: Box<[Option]>, - has_trailing_comma_spread: bool, -} - -impl ArrayLiteral { - /// Crate a new array literal. - pub(crate) fn new(array: A, has_trailing_comma_spread: bool) -> Self - where - A: Into]>>, - { - Self { - arr: array.into(), - has_trailing_comma_spread, - } - } - - /// Indicates if a spread operator in the array literal has a trailing comma. - /// This is a syntax error in some cases. - pub(crate) fn has_trailing_comma_spread(&self) -> bool { - self.has_trailing_comma_spread - } - - #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - self.arr - .iter() - .flatten() - .any(Expression::contains_arguments) - } - - #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.arr.iter().flatten().any(|expr| expr.contains(symbol)) - } -} - -impl AsRef<[Option]> for ArrayLiteral { - #[inline] - fn as_ref(&self) -> &[Option] { - &self.arr - } -} - -impl From for ArrayLiteral -where - T: Into]>>, -{ - #[inline] - fn from(decl: T) -> Self { - Self { - arr: decl.into(), - has_trailing_comma_spread: false, - } - } -} - -impl ToInternedString for ArrayLiteral { - #[inline] - fn to_interned_string(&self, interner: &Interner) -> String { - let mut buf = String::from("["); - let mut first = true; - for e in &*self.arr { - if first { - first = false; - } else { - buf.push_str(", "); - } - if let Some(e) = e { - buf.push_str(&e.to_interned_string(interner)); - } - } - buf.push(']'); - buf - } -} - -impl From for Expression { - #[inline] - fn from(arr: ArrayLiteral) -> Self { - Self::ArrayLiteral(arr) - } -} - -impl VisitWith for ArrayLiteral { - fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow - where - V: Visitor<'a>, - { - for expr in self.arr.iter().flatten() { - try_break!(visitor.visit_expression(expr)); - } - ControlFlow::Continue(()) - } - - fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow - where - V: VisitorMut<'a>, - { - for expr in self.arr.iter_mut().flatten() { - try_break!(visitor.visit_expression_mut(expr)); - } - ControlFlow::Continue(()) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let a = [1, 2, 3, "words", "more words"]; - let b = []; - "#, - ); - } -} diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs deleted file mode 100644 index e3e410feac..0000000000 --- a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs +++ /dev/null @@ -1,180 +0,0 @@ -//! Object Expression. - -#[cfg(test)] -mod tests; - -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::ast::{ - block_to_string, - expression::Expression, - join_nodes, - property::{MethodDefinition, PropertyDefinition}, - ContainsSymbol, -}; -use crate::try_break; -use boa_interner::{Interner, ToIndentedString, ToInternedString}; -use core::ops::ControlFlow; - -/// Objects in JavaScript may be defined as an unordered collection of related data, of -/// primitive or reference types, in the form of “key: value” pairs. -/// -/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal -/// notation. -/// -/// An object initializer is an expression that describes the initialization of an -/// [`Object`][object]. Objects consist of properties, which are used to describe an object. -/// Values of object properties can either contain [`primitive`][primitive] data types or other -/// objects. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer -/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object -/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "deser", serde(transparent))] -#[derive(Clone, Debug, PartialEq)] -pub struct ObjectLiteral { - properties: Box<[PropertyDefinition]>, -} - -impl ObjectLiteral { - /// Gets the object literal properties - #[inline] - pub fn properties(&self) -> &[PropertyDefinition] { - &self.properties - } - - #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - self.properties - .iter() - .any(PropertyDefinition::contains_arguments) - } - - #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.properties.iter().any(|prop| prop.contains(symbol)) - } -} - -impl ToIndentedString for ObjectLiteral { - fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { - let mut buf = "{\n".to_owned(); - let indentation = " ".repeat(indent_n + 1); - for property in self.properties().iter() { - buf.push_str(&match property { - PropertyDefinition::IdentifierReference(ident) => { - format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) - } - PropertyDefinition::Property(key, value) => { - format!( - "{indentation}{}: {},\n", - key.to_interned_string(interner), - value.to_no_indent_string(interner, indent_n + 1) - ) - } - PropertyDefinition::SpreadObject(key) => { - format!("{indentation}...{},\n", key.to_interned_string(interner)) - } - PropertyDefinition::MethodDefinition(key, method) => { - format!( - "{indentation}{}{}({}) {},\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - key.to_interned_string(interner), - match &method { - MethodDefinition::Get(expression) - | MethodDefinition::Set(expression) - | MethodDefinition::Ordinary(expression) => { - join_nodes(interner, &expression.parameters().parameters) - } - MethodDefinition::Generator(expression) => { - join_nodes(interner, &expression.parameters().parameters) - } - MethodDefinition::AsyncGenerator(expression) => { - join_nodes(interner, &expression.parameters().parameters) - } - MethodDefinition::Async(expression) => { - join_nodes(interner, &expression.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(expression) - | MethodDefinition::Set(expression) - | MethodDefinition::Ordinary(expression) => { - block_to_string(expression.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(expression) => { - block_to_string(expression.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(expression) => { - block_to_string(expression.body(), interner, indent_n + 1) - } - MethodDefinition::Async(expression) => { - block_to_string(expression.body(), interner, indent_n + 1) - } - }, - ) - } - PropertyDefinition::CoverInitializedName(ident, expr) => { - format!( - "{indentation}{} = {},\n", - interner.resolve_expect(ident.sym()), - expr.to_no_indent_string(interner, indent_n + 1) - ) - } - }); - } - buf.push_str(&format!("{}}}", " ".repeat(indent_n))); - - buf - } -} - -impl From for ObjectLiteral -where - T: Into>, -{ - #[inline] - fn from(props: T) -> Self { - Self { - properties: props.into(), - } - } -} - -impl From for Expression { - #[inline] - fn from(obj: ObjectLiteral) -> Self { - Self::ObjectLiteral(obj) - } -} - -impl VisitWith for ObjectLiteral { - fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow - where - V: Visitor<'a>, - { - for pd in self.properties.iter() { - try_break!(visitor.visit_property_definition(pd)); - } - ControlFlow::Continue(()) - } - - fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow - where - V: VisitorMut<'a>, - { - for pd in self.properties.iter_mut() { - try_break!(visitor.visit_property_definition_mut(pd)); - } - ControlFlow::Continue(()) - } -} diff --git a/boa_engine/src/syntax/ast/expression/literal/object/tests.rs b/boa_engine/src/syntax/ast/expression/literal/object/tests.rs deleted file mode 100644 index 3f078f5645..0000000000 --- a/boa_engine/src/syntax/ast/expression/literal/object/tests.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::exec; - -#[test] -fn spread_shallow_clone() { - let scenario = r#" - var a = { x: {} }; - var aClone = { ...a }; - - a.x === aClone.x - "#; - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn spread_merge() { - let scenario = r#" - var a = { x: 1, y: 2 }; - var b = { x: -1, z: -3, ...a }; - - (b.x === 1) && (b.y === 2) && (b.z === -3) - "#; - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn spread_overriding_properties() { - let scenario = r#" - var a = { x: 0, y: 0 }; - var aWithOverrides = { ...a, ...{ x: 1, y: 2 } }; - - (aWithOverrides.x === 1) && (aWithOverrides.y === 2) - "#; - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn spread_getters_in_initializer() { - let scenario = r#" - var a = { x: 42 }; - var aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } }; - "#; - assert_eq!(&exec(scenario), "undefined"); -} - -#[test] -fn spread_getters_in_object() { - let scenario = r#" - var a = { x: 42 }; - var aWithXGetter = { ...a, ... { get x() { throw new Error('not thrown yet') } } }; - "#; - assert_eq!(&exec(scenario), "\"Error\": \"not thrown yet\""); -} - -#[test] -fn spread_setters() { - let scenario = r#" - var z = { set x(nexX) { throw new Error() }, ... { x: 1 } }; - "#; - assert_eq!(&exec(scenario), "undefined"); -} - -#[test] -fn spread_null_and_undefined_ignored() { - let scenario = r#" - var a = { ...null, ...undefined }; - var count = 0; - - for (key in a) { count++; } - - count === 0 - "#; - - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let other = { - c: 10, - }; - let inst = { - val: 5, - b: "hello world", - nested: { - a: 5, - b: 6, - }, - ...other, - say_hi: function() { - console.log("hello!"); - }, - get a() { - return this.val + 1; - }, - set a(new_value) { - this.val = new_value; - }, - say_hello(msg) { - console.log("hello " + msg); - }, - }; - inst.a = 20; - inst.a; - inst.say_hello("humans"); - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs deleted file mode 100644 index bc6d339245..0000000000 --- a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs +++ /dev/null @@ -1,475 +0,0 @@ -//! Assignment expression nodes, as defined by the [spec]. -//! -//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right -//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple -//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=` -//! only allow ["simple"][simple] left hand side expressions as an assignment target. -//! -//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression -//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators -//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression -//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype - -mod op; - -use core::ops::ControlFlow; -pub use op::*; - -use boa_interner::{Interner, Sym, ToInternedString}; - -use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut}; -use crate::syntax::{ - ast::{ - expression::{ - access::PropertyAccess, - identifier::Identifier, - literal::{ArrayLiteral, ObjectLiteral}, - Expression, - }, - pattern::{ - ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern, - }, - property::{PropertyDefinition, PropertyName}, - ContainsSymbol, - }, - parser::RESERVED_IDENTIFIERS_STRICT, -}; -use crate::try_break; - -/// An assignment operator expression. -/// -/// See the [module level documentation][self] for more information. -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct Assign { - op: AssignOp, - lhs: Box, - rhs: Box, -} - -impl Assign { - /// Creates an `Assign` AST Expression. - pub(in crate::syntax) fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self { - Self { - op, - lhs: Box::new(lhs), - rhs: Box::new(rhs), - } - } - - /// Gets the operator of the assignment operation. - #[inline] - pub fn op(&self) -> AssignOp { - self.op - } - - /// Gets the left hand side of the assignment operation. - #[inline] - pub fn lhs(&self) -> &AssignTarget { - &self.lhs - } - - /// Gets the right hand side of the assignment operation. - #[inline] - pub fn rhs(&self) -> &Expression { - &self.rhs - } - - #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - (match &*self.lhs { - AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS, - AssignTarget::Access(access) => access.contains_arguments(), - AssignTarget::Pattern(pattern) => pattern.contains_arguments(), - } || self.rhs.contains_arguments()) - } - - #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - (match &*self.lhs { - AssignTarget::Identifier(_) => false, - AssignTarget::Access(access) => access.contains(symbol), - AssignTarget::Pattern(pattern) => pattern.contains(symbol), - } || self.rhs.contains(symbol)) - } -} - -impl ToInternedString for Assign { - #[inline] - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{} {} {}", - self.lhs.to_interned_string(interner), - self.op, - self.rhs.to_interned_string(interner) - ) - } -} - -impl From for Expression { - #[inline] - fn from(op: Assign) -> Self { - Self::Assign(op) - } -} - -impl VisitWith for Assign { - fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow - where - V: Visitor<'a>, - { - try_break!(visitor.visit_assign_target(&self.lhs)); - visitor.visit_expression(&self.rhs) - } - - fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow - where - V: VisitorMut<'a>, - { - try_break!(visitor.visit_assign_target_mut(&mut self.lhs)); - visitor.visit_expression_mut(&mut self.rhs) - } -} - -/// The valid left-hand-side expressions of an assignment operator. Also called -/// [`LeftHandSideExpression`][spec] in the spec. -/// -/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression -#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum AssignTarget { - /// A simple identifier, such as `a`. - Identifier(Identifier), - /// A property access, such as `a.prop`. - Access(PropertyAccess), - /// A pattern assignment, such as `{a, b, ...c}`. - Pattern(Pattern), -} - -impl AssignTarget { - /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`]. - /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression. - pub(crate) fn from_expression( - expression: &Expression, - strict: bool, - destructure: bool, - ) -> Option { - match expression { - Expression::Identifier(id) => Some(Self::Identifier(*id)), - Expression::PropertyAccess(access) => Some(Self::Access(access.clone())), - Expression::ObjectLiteral(object) if destructure => { - let pattern = object_decl_to_declaration_pattern(object, strict)?; - Some(Self::Pattern(pattern.into())) - } - Expression::ArrayLiteral(array) if destructure => { - let pattern = array_decl_to_declaration_pattern(array, strict)?; - Some(Self::Pattern(pattern.into())) - } - _ => None, - } - } -} - -impl ToInternedString for AssignTarget { - #[inline] - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - AssignTarget::Identifier(id) => id.to_interned_string(interner), - AssignTarget::Access(access) => access.to_interned_string(interner), - AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner), - } - } -} - -impl From for AssignTarget { - #[inline] - fn from(target: Identifier) -> Self { - Self::Identifier(target) - } -} - -/// Converts an object literal into an object declaration pattern. -pub(crate) fn object_decl_to_declaration_pattern( - object: &ObjectLiteral, - strict: bool, -) -> Option { - let mut bindings = Vec::new(); - let mut excluded_keys = Vec::new(); - for (i, property) in object.properties().iter().enumerate() { - match property { - PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { - return None - } - PropertyDefinition::IdentifierReference(ident) => { - if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) { - return None; - } - - excluded_keys.push(*ident); - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(ident.sym()), - default_init: None, - }); - } - PropertyDefinition::Property(name, expr) => match (name, expr) { - (PropertyName::Literal(name), Expression::Identifier(ident)) if *name == *ident => { - if strict && *name == Sym::EVAL { - return None; - } - if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) { - return None; - } - - excluded_keys.push(*ident); - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(*name), - default_init: None, - }); - } - (PropertyName::Literal(name), Expression::Identifier(ident)) => { - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(*name), - default_init: None, - }); - } - (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?.into(); - bindings.push(ObjectPatternElement::Pattern { - name: PropertyName::Literal(*name), - pattern, - default_init: None, - }); - } - (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); - bindings.push(ObjectPatternElement::Pattern { - name: PropertyName::Literal(*name), - pattern, - default_init: None, - }); - } - (_, Expression::Assign(assign)) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - if let Some(name) = name.literal() { - if name == *ident { - if strict && name == Sym::EVAL { - return None; - } - if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { - return None; - } - excluded_keys.push(*ident); - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); - } else { - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); - } - } else { - return None; - } - } - AssignTarget::Pattern(pattern) => { - bindings.push(ObjectPatternElement::Pattern { - name: name.clone(), - pattern: pattern.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::Access(access) => { - bindings.push(ObjectPatternElement::AssignmentPropertyAccess { - name: name.clone(), - access: access.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - }, - (_, Expression::PropertyAccess(access)) => { - bindings.push(ObjectPatternElement::AssignmentPropertyAccess { - name: name.clone(), - access: access.clone(), - default_init: None, - }); - } - (PropertyName::Computed(name), Expression::Identifier(ident)) => { - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Computed(name.clone()), - default_init: None, - }); - } - _ => return None, - }, - PropertyDefinition::SpreadObject(spread) => { - match spread { - Expression::Identifier(ident) => { - bindings.push(ObjectPatternElement::RestProperty { - ident: *ident, - excluded_keys: excluded_keys.clone(), - }); - } - Expression::PropertyAccess(access) => { - bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess { - access: access.clone(), - excluded_keys: excluded_keys.clone(), - }); - } - _ => return None, - } - if i + 1 != object.properties().len() { - return None; - } - } - PropertyDefinition::MethodDefinition(_, _) => return None, - PropertyDefinition::CoverInitializedName(ident, expr) => { - if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) { - return None; - } - - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(ident.sym()), - default_init: Some(expr.clone()), - }); - } - } - } - Some(ObjectPattern::new(bindings.into())) -} - -/// Converts an array declaration into an array declaration pattern. -pub(crate) fn array_decl_to_declaration_pattern( - array: &ArrayLiteral, - strict: bool, -) -> Option { - if array.has_trailing_comma_spread() { - return None; - } - - let mut bindings = Vec::new(); - for (i, expr) in array.as_ref().iter().enumerate() { - let expr = if let Some(expr) = expr { - expr - } else { - bindings.push(ArrayPatternElement::Elision); - continue; - }; - match expr { - Expression::Identifier(ident) => { - if strict && *ident == Sym::ARGUMENTS { - return None; - } - - bindings.push(ArrayPatternElement::SingleName { - ident: *ident, - default_init: None, - }); - } - Expression::Spread(spread) => { - match spread.target() { - Expression::Identifier(ident) => { - bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident }); - } - Expression::PropertyAccess(access) => { - bindings.push(ArrayPatternElement::PropertyAccessRest { - access: access.clone(), - }); - } - Expression::ArrayLiteral(array) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); - bindings.push(ArrayPatternElement::PatternRest { pattern }); - } - Expression::ObjectLiteral(object) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?.into(); - bindings.push(ArrayPatternElement::PatternRest { pattern }); - } - _ => return None, - } - if i + 1 != array.as_ref().len() { - return None; - } - } - Expression::Assign(assign) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - bindings.push(ArrayPatternElement::SingleName { - ident: *ident, - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::Access(access) => { - bindings.push(ArrayPatternElement::PropertyAccess { - access: access.clone(), - }); - } - AssignTarget::Pattern(pattern) => match pattern { - Pattern::Object(pattern) => { - bindings.push(ArrayPatternElement::Pattern { - pattern: Pattern::Object(pattern.clone()), - default_init: Some(assign.rhs().clone()), - }); - } - Pattern::Array(pattern) => { - bindings.push(ArrayPatternElement::Pattern { - pattern: Pattern::Array(pattern.clone()), - default_init: Some(assign.rhs().clone()), - }); - } - }, - }, - Expression::ArrayLiteral(array) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); - bindings.push(ArrayPatternElement::Pattern { - pattern, - default_init: None, - }); - } - Expression::ObjectLiteral(object) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?.into(); - bindings.push(ArrayPatternElement::Pattern { - pattern, - default_init: None, - }); - } - Expression::PropertyAccess(access) => { - bindings.push(ArrayPatternElement::PropertyAccess { - access: access.clone(), - }); - } - _ => return None, - } - } - Some(ArrayPattern::new(bindings.into())) -} - -impl VisitWith for AssignTarget { - fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow - where - V: Visitor<'a>, - { - match self { - AssignTarget::Identifier(id) => visitor.visit_identifier(id), - AssignTarget::Access(pa) => visitor.visit_property_access(pa), - AssignTarget::Pattern(pat) => visitor.visit_pattern(pat), - } - } - - fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow - where - V: VisitorMut<'a>, - { - match self { - AssignTarget::Identifier(id) => visitor.visit_identifier_mut(id), - AssignTarget::Access(pa) => visitor.visit_property_access_mut(pa), - AssignTarget::Pattern(pat) => visitor.visit_pattern_mut(pat), - } - } -} diff --git a/boa_engine/src/syntax/ast/expression/operator/tests.rs b/boa_engine/src/syntax/ast/expression/operator/tests.rs deleted file mode 100644 index 071fc2eb9a..0000000000 --- a/boa_engine/src/syntax/ast/expression/operator/tests.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::exec; - -#[test] -fn assignmentoperator_lhs_not_defined() { - let scenario = r#" - try { - a += 5 - } catch (err) { - err.toString() - } - "#; - - assert_eq!(&exec(scenario), "\"ReferenceError: a is not defined\""); -} - -#[test] -fn assignmentoperator_rhs_throws_error() { - let scenario = r#" - try { - let a; - a += b - } catch (err) { - err.toString() - } - "#; - - assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\""); -} - -#[test] -fn instanceofoperator_rhs_not_object() { - let scenario = r#" - try { - let s = new String(); - s instanceof 1 - } catch (err) { - err.toString() - } - "#; - - assert_eq!( - &exec(scenario), - "\"TypeError: right-hand side of 'instanceof' should be an object, got number\"" - ); -} - -#[test] -fn instanceofoperator_rhs_not_callable() { - let scenario = r#" - try { - let s = new String(); - s instanceof {} - } catch (err) { - err.toString() - } - "#; - - assert_eq!( - &exec(scenario), - "\"TypeError: right-hand side of 'instanceof' is not callable\"" - ); -} - -#[test] -fn logical_nullish_assignment() { - let scenario = r#" - let a = undefined; - a ??= 10; - a; - "#; - - assert_eq!(&exec(scenario), "10"); - - let scenario = r#" - let a = 20; - a ??= 10; - a; - "#; - - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn logical_assignment() { - let scenario = r#" - let a = false; - a &&= 10; - a; - "#; - - assert_eq!(&exec(scenario), "false"); - - let scenario = r#" - let a = 20; - a &&= 10; - a; - "#; - - assert_eq!(&exec(scenario), "10"); - - let scenario = r#" - let a = null; - a ||= 10; - a; - "#; - - assert_eq!(&exec(scenario), "10"); - let scenario = r#" - let a = 20; - a ||= 10; - a; - "#; - - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let a = 20; - a += 10; - a -= 10; - a *= 10; - a **= 10; - a /= 10; - a %= 10; - a &= 10; - a |= 10; - a ^= 10; - a <<= 10; - a >>= 10; - a >>>= 10; - a &&= 10; - a ||= 10; - a ??= 10; - a; - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/statement/iteration/tests.rs b/boa_engine/src/syntax/ast/statement/iteration/tests.rs deleted file mode 100644 index 98859f6a9b..0000000000 --- a/boa_engine/src/syntax/ast/statement/iteration/tests.rs +++ /dev/null @@ -1,558 +0,0 @@ -use crate::{check_output, exec, syntax::ast::test_formatting, TestAction}; - -#[test] -fn while_loop_late_break() { - // Ordering with statement before the break. - let scenario = r#" - let a = 1; - while (a < 5) { - a++; - if (a == 3) { - break; - } - } - a; - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn while_loop_early_break() { - // Ordering with statements after the break. - let scenario = r#" - let a = 1; - while (a < 5) { - if (a == 3) { - break; - } - a++; - } - a; - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn for_loop_break() { - let scenario = r#" - let a = 1; - for (; a < 5; a++) { - if (a == 3) { - break; - } - } - a; - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn for_loop_return() { - let scenario = r#" - function foo() { - for (let a = 1; a < 5; a++) { - if (a == 3) { - return a; - } - } - } - - foo(); - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn do_loop_late_break() { - // Ordering with statement before the break. - let scenario = r#" - let a = 1; - do { - a++; - if (a == 3) { - break; - } - } while (a < 5); - a; - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn do_loop_early_break() { - // Ordering with statements after the break. - let scenario = r#" - let a = 1; - do { - if (a == 3) { - break; - } - a++; - } while (a < 5); - a; - "#; - - assert_eq!(&exec(scenario), "3"); -} - -#[test] -fn break_out_of_inner_loop() { - let scenario = r#" - var a = 0, b = 0; - for (let i = 0; i < 2; i++) { - a++; - for (let j = 0; j < 10; j++) { - b++; - if (j == 3) - break; - } - a++; - } - [a, b] - "#; - assert_eq!(&exec(scenario), "[ 4, 8 ]"); -} - -#[test] -fn continue_inner_loop() { - let scenario = r#" - var a = 0, b = 0; - for (let i = 0; i < 2; i++) { - a++; - for (let j = 0; j < 10; j++) { - if (j < 3) - continue; - b++; - } - a++; - } - [a, b] - "#; - assert_eq!(&exec(scenario), "[ 4, 14 ]"); -} - -#[test] -fn for_loop_continue_out_of_switch() { - let scenario = r#" - var a = 0, b = 0, c = 0; - for (let i = 0; i < 3; i++) { - a++; - switch (i) { - case 0: - continue; - c++; - case 1: - continue; - case 5: - c++; - } - b++; - } - [a, b, c] - "#; - assert_eq!(&exec(scenario), "[ 3, 1, 0 ]"); -} - -#[test] -fn while_loop_continue() { - let scenario = r#" - var i = 0, a = 0, b = 0; - while (i < 3) { - i++; - if (i < 2) { - a++; - continue; - } - b++; - } - [a, b] - "#; - assert_eq!(&exec(scenario), "[ 1, 2 ]"); -} - -#[test] -fn do_while_loop_continue() { - let scenario = r#" - var i = 0, a = 0, b = 0; - do { - i++; - if (i < 2) { - a++; - continue; - } - b++; - } while (i < 3) - [a, b] - "#; - assert_eq!(&exec(scenario), "[ 1, 2 ]"); -} - -#[test] -fn for_of_loop_declaration() { - let scenario = r#" - var result = 0; - for (i of [1, 2, 3]) { - result = i; - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "3"), - TestAction::TestEq("i", "3"), - ]); -} - -#[test] -fn for_of_loop_var() { - let scenario = r#" - var result = 0; - for (var i of [1, 2, 3]) { - result = i; - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "3"), - TestAction::TestEq("i", "3"), - ]); -} - -#[test] -fn for_of_loop_let() { - let scenario = r#" - var result = 0; - for (let i of [1, 2, 3]) { - result = i; - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "3"), - TestAction::TestEq( - r#" - try { - i - } catch(e) { - e.toString() - } - "#, - "\"ReferenceError: i is not defined\"", - ), - ]); -} - -#[test] -fn for_of_loop_const() { - let scenario = r#" - var result = 0; - for (let i of [1, 2, 3]) { - result = i; - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "3"), - TestAction::TestEq( - r#" - try { - i - } catch(e) { - e.toString() - } - "#, - "\"ReferenceError: i is not defined\"", - ), - ]); -} - -#[test] -fn for_of_loop_break() { - let scenario = r#" - var result = 0; - for (var i of [1, 2, 3]) { - if (i > 1) - break; - result = i - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "1"), - TestAction::TestEq("i", "2"), - ]); -} - -#[test] -fn for_of_loop_continue() { - let scenario = r#" - var result = 0; - for (var i of [1, 2, 3]) { - if (i == 3) - continue; - result = i - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("result", "2"), - TestAction::TestEq("i", "3"), - ]); -} - -#[test] -fn for_of_loop_return() { - let scenario = r#" - function foo() { - for (i of [1, 2, 3]) { - if (i > 1) - return i; - } - } - "#; - check_output(&[ - TestAction::Execute(scenario), - TestAction::TestEq("foo()", "2"), - ]); -} - -#[test] -fn for_loop_break_label() { - let scenario = r#" - var str = ""; - - outer: for (let i = 0; i < 5; i++) { - inner: for (let b = 0; b < 5; b++) { - if (b === 2) { - break outer; - } - str = str + b; - } - str = str + i; - } - str - "#; - assert_eq!(&exec(scenario), "\"01\""); -} - -#[test] -fn for_loop_continue_label() { - let scenario = r#" - var count = 0; - label: for (let x = 0; x < 10;) { - while (true) { - x++; - count++; - continue label; - } - } - count - "#; - assert_eq!(&exec(scenario), "10"); -} - -#[test] -fn for_in_declaration() { - let init = r#" - let result = []; - let obj = { a: "a", b: 2}; - var i; - for (i in obj) { - result = result.concat([i]); - } - "#; - check_output(&[ - TestAction::Execute(init), - TestAction::TestEq( - "result.length === 2 && result.includes('a') && result.includes('b')", - "true", - ), - ]); -} - -#[test] -fn for_in_var_object() { - let init = r#" - let result = []; - let obj = { a: "a", b: 2}; - for (var i in obj) { - result = result.concat([i]); - } - "#; - check_output(&[ - TestAction::Execute(init), - TestAction::TestEq( - "result.length === 2 && result.includes('a') && result.includes('b')", - "true", - ), - ]); -} - -#[test] -fn for_in_var_array() { - let init = r#" - let result = []; - let arr = ["a", "b"]; - for (var i in arr) { - result = result.concat([i]); - } - "#; - check_output(&[ - TestAction::Execute(init), - TestAction::TestEq( - "result.length === 2 && result.includes('0') && result.includes('1')", - "true", - ), - ]); -} - -#[test] -fn for_in_let_object() { - let init = r#" - let result = []; - let obj = { a: "a", b: 2}; - for (let i in obj) { - result = result.concat([i]); - } - "#; - check_output(&[ - TestAction::Execute(init), - TestAction::TestEq( - "result.length === 2 && result.includes('a') && result.includes('b')", - "true", - ), - ]); -} - -#[test] -fn for_in_const_array() { - let init = r#" - let result = []; - let arr = ["a", "b"]; - for (const i in arr) { - result = result.concat([i]); - } - "#; - check_output(&[ - TestAction::Execute(init), - TestAction::TestEq( - "result.length === 2 && result.includes('0') && result.includes('1')", - "true", - ), - ]); -} - -#[test] -fn for_in_break_label() { - let scenario = r#" - var str = ""; - - outer: for (let i in [1, 2]) { - inner: for (let b in [2, 3, 4]) { - if (b === "1") { - break outer; - } - str = str + b; - } - str = str + i; - } - str - "#; - assert_eq!(&exec(scenario), "\"0\""); -} - -#[test] -fn for_in_continue_label() { - let scenario = r#" - var str = ""; - - outer: for (let i in [1, 2]) { - inner: for (let b in [2, 3, 4]) { - if (b === "1") { - continue outer; - } - str = str + b; - } - str = str + i; - } - str - "#; - assert_eq!(&exec(scenario), "\"00\""); -} - -#[test] -fn fmt() { - // Labeled and unlabeled for in loops - test_formatting( - r#" - var str = ""; - outer: for (let i in [1, 2]) { - for (let b in [2, 3, 4]) { - if (b === "1") { - continue outer; - } - str = str + b; - } - str = str + i; - } - str; - "#, - ); - // Labeled and unlabeled for loops - test_formatting( - r#" - var str = ""; - outer: for (let i = 0; i < 10; ++i) { - for (let j = 3; j < 6; ++j) { - if (j === "1") { - continue outer; - } - str = str + j; - } - str = str + i; - } - str; - "#, - ); - // Labeled and unlabeled for of loops - test_formatting( - r#" - for (i of [1, 2, 3]) { - if (false) { - break; - } - } - label: for (i of [1, 2, 3]) { - if (false) { - break label; - } - } - "#, - ); - // Labeled and unlabeled do while loops - test_formatting( - r#" - do { - break; - } while (true); - label: do { - break label; - } while (true); - "#, - ); - // Labeled and unlabeled while loops - test_formatting( - r#" - while (true) { - break; - } - label: while (true) { - break label; - } - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/statement/switch/tests.rs b/boa_engine/src/syntax/ast/statement/switch/tests.rs deleted file mode 100644 index b9182b831d..0000000000 --- a/boa_engine/src/syntax/ast/statement/switch/tests.rs +++ /dev/null @@ -1,243 +0,0 @@ -use crate::exec; - -#[test] -fn single_case_switch() { - let scenario = r#" - let a = 10; - switch (a) { - case 10: - a = 20; - break; - } - - a; - "#; - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn no_cases_switch() { - let scenario = r#" - let a = 10; - switch (a) { - } - - a; - "#; - assert_eq!(&exec(scenario), "10"); -} - -#[test] -fn no_true_case_switch() { - let scenario = r#" - let a = 10; - switch (a) { - case 5: - a = 15; - break; - } - - a; - "#; - assert_eq!(&exec(scenario), "10"); -} - -#[test] -fn two_case_switch() { - let scenario = r#" - let a = 10; - switch (a) { - case 5: - a = 15; - break; - case 10: - a = 20; - break; - } - - a; - "#; - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn two_case_no_break_switch() { - let scenario = r#" - let a = 10; - let b = 10; - - switch (a) { - case 10: - a = 150; - case 20: - b = 150; - break; - } - - a + b; - "#; - assert_eq!(&exec(scenario), "300"); -} - -#[test] -fn three_case_partial_fallthrough() { - let scenario = r#" - let a = 10; - let b = 10; - - switch (a) { - case 10: - a = 150; - case 20: - b = 150; - break; - case 15: - b = 1000; - break; - } - - a + b; - "#; - assert_eq!(&exec(scenario), "300"); -} - -#[test] -fn default_taken_switch() { - let scenario = r#" - let a = 10; - - switch (a) { - case 5: - a = 150; - break; - default: - a = 70; - } - - a; - "#; - assert_eq!(&exec(scenario), "70"); -} - -#[test] -fn default_not_taken_switch() { - let scenario = r#" - let a = 5; - - switch (a) { - case 5: - a = 150; - break; - default: - a = 70; - } - - a; - "#; - assert_eq!(&exec(scenario), "150"); -} - -#[test] -fn string_switch() { - let scenario = r#" - let a = "hello"; - - switch (a) { - case "hello": - a = "world"; - break; - default: - a = "hi"; - } - - a; - "#; - assert_eq!(&exec(scenario), "\"world\""); -} - -#[test] -fn bigger_switch_example() { - let expected = [ - "\"Mon\"", - "\"Tue\"", - "\"Wed\"", - "\"Thurs\"", - "\"Fri\"", - "\"Sat\"", - "\"Sun\"", - ]; - - for (i, val) in expected.iter().enumerate() { - let scenario = format!( - r#" - let a = {i}; - let b = "unknown"; - - switch (a) {{ - case 0: - b = "Mon"; - break; - case 1: - b = "Tue"; - break; - case 2: - b = "Wed"; - break; - case 3: - b = "Thurs"; - break; - case 4: - b = "Fri"; - break; - case 5: - b = "Sat"; - break; - case 6: - b = "Sun"; - break; - }} - - b; - - "#, - ); - - assert_eq!(&exec(&scenario), val); - } -} - -#[test] -fn fmt() { - crate::syntax::ast::test_formatting( - r#" - let a = 3; - let b = "unknown"; - switch (a) { - case 0: - b = "Mon"; - break; - case 1: - b = "Tue"; - break; - case 2: - b = "Wed"; - break; - case 3: - b = "Thurs"; - break; - case 4: - b = "Fri"; - break; - case 5: - b = "Sat"; - break; - case 6: - b = "Sun"; - break; - default: - b = "Unknown"; - } - b; - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/statement/try/tests.rs b/boa_engine/src/syntax/ast/statement/try/tests.rs deleted file mode 100644 index 4cd55ebe8f..0000000000 --- a/boa_engine/src/syntax/ast/statement/try/tests.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::exec; - -#[test] -fn simple_try() { - let scenario = r#" - let a = 10; - try { - a = 20; - } catch { - a = 30; - } - - a; - "#; - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn finally() { - let scenario = r#" - let a = 10; - try { - a = 20; - } finally { - a = 30; - } - - a; - "#; - assert_eq!(&exec(scenario), "30"); -} - -#[test] -fn catch_finally() { - let scenario = r#" - let a = 10; - try { - a = 20; - } catch { - a = 40; - } finally { - a = 30; - } - - a; - "#; - assert_eq!(&exec(scenario), "30"); -} - -#[test] -fn catch() { - let scenario = r#" - let a = 10; - try { - throw "error"; - } catch { - a = 20; - } - - a; - "#; - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn catch_binding() { - let scenario = r#" - let a = 10; - try { - throw 20; - } catch(err) { - a = err; - } - - a; - "#; - assert_eq!(&exec(scenario), "20"); -} - -#[test] -fn catch_binding_pattern_object() { - let scenario = r#" - let a = 10; - try { - throw { - n: 30, - }; - } catch ({ n }) { - a = n; - } - - a; - "#; - assert_eq!(&exec(scenario), "30"); -} - -#[test] -fn catch_binding_pattern_array() { - let scenario = r#" - let a = 10; - try { - throw [20, 30]; - } catch ([, n]) { - a = n; - } - - a; - "#; - assert_eq!(&exec(scenario), "30"); -} - -#[test] -fn catch_binding_finally() { - let scenario = r#" - let a = 10; - try { - throw 20; - } catch(err) { - a = err; - } finally { - a = 30; - } - - a; - "#; - assert_eq!(&exec(scenario), "30"); -} - -#[test] -fn fmt() { - crate::syntax::ast::test_formatting( - r#" - try { - throw "hello"; - } catch(e) { - console.log(e); - } finally { - console.log("things"); - } - try { - throw "hello"; - } catch { - console.log("something went wrong"); - } - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/statement_list/tests.rs b/boa_engine/src/syntax/ast/statement_list/tests.rs deleted file mode 100644 index 71df252646..0000000000 --- a/boa_engine/src/syntax/ast/statement_list/tests.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::exec; - -#[test] -fn strict_mode_global() { - let scenario = r#" - 'use strict'; - let throws = false; - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - throws - "#; - - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn strict_mode_function() { - let scenario = r#" - let throws = false; - function t() { - 'use strict'; - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - } - t() - throws - "#; - - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn strict_mode_function_after() { - let scenario = r#" - function t() { - 'use strict'; - } - t() - let throws = false; - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - throws - "#; - - assert_eq!(&exec(scenario), "false"); -} - -#[test] -fn strict_mode_global_active_in_function() { - let scenario = r#" - 'use strict' - let throws = false; - function a(){ - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - } - a(); - throws - "#; - - assert_eq!(&exec(scenario), "true"); -} - -#[test] -fn strict_mode_function_in_function() { - let scenario = r#" - let throws = false; - function a(){ - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - } - function b(){ - 'use strict'; - a(); - } - b(); - throws - "#; - - assert_eq!(&exec(scenario), "false"); -} - -#[test] -fn strict_mode_function_return() { - let scenario = r#" - let throws = false; - function a() { - 'use strict'; - - return function () { - try { - delete Boolean.prototype; - } catch (e) { - throws = true; - } - } - } - a()(); - throws - "#; - - assert_eq!(&exec(scenario), "true"); -} diff --git a/boa_engine/src/syntax/lexer/comment.rs b/boa_engine/src/syntax/lexer/comment.rs index a34bce9fbc..bc0a1d2f67 100644 --- a/boa_engine/src/syntax/lexer/comment.rs +++ b/boa_engine/src/syntax/lexer/comment.rs @@ -1,10 +1,8 @@ //! This module implements lexing for comments used in the JavaScript programing language. use super::{Cursor, Error, Tokenizer}; -use crate::syntax::{ - ast::{Position, Span}, - lexer::{Token, TokenKind}, -}; +use crate::syntax::lexer::{Token, TokenKind}; +use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/lexer/cursor.rs b/boa_engine/src/syntax/lexer/cursor.rs index e59d0e8e15..79d4fe2454 100644 --- a/boa_engine/src/syntax/lexer/cursor.rs +++ b/boa_engine/src/syntax/lexer/cursor.rs @@ -1,5 +1,5 @@ //! Module implementing the lexer cursor. This is used for managing the input byte stream. -use crate::syntax::ast::Position; +use boa_ast::Position; use boa_profiler::Profiler; use std::io::{self, Bytes, Error, ErrorKind, Read}; diff --git a/boa_engine/src/syntax/lexer/error.rs b/boa_engine/src/syntax/lexer/error.rs index d6082cde45..d24044aa9a 100644 --- a/boa_engine/src/syntax/lexer/error.rs +++ b/boa_engine/src/syntax/lexer/error.rs @@ -5,7 +5,7 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard -use super::Position; +use boa_ast::Position; use std::{error::Error as StdError, fmt, io}; #[derive(Debug)] diff --git a/boa_engine/src/syntax/lexer/identifier.rs b/boa_engine/src/syntax/lexer/identifier.rs index e44d697e3e..2520281039 100644 --- a/boa_engine/src/syntax/lexer/identifier.rs +++ b/boa_engine/src/syntax/lexer/identifier.rs @@ -1,10 +1,8 @@ //! This module implements lexing for identifiers (foo, myvar, etc.) used in the JavaScript programing language. use super::{Cursor, Error, Tokenizer}; -use crate::syntax::{ - ast::{Keyword, Position, Span}, - lexer::{StringLiteral, Token, TokenKind}, -}; +use crate::syntax::lexer::{StringLiteral, Token, TokenKind}; +use boa_ast::{Keyword, Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use boa_unicode::UnicodeProperties; diff --git a/boa_engine/src/syntax/lexer/mod.rs b/boa_engine/src/syntax/lexer/mod.rs index f122239b42..de06419806 100644 --- a/boa_engine/src/syntax/lexer/mod.rs +++ b/boa_engine/src/syntax/lexer/mod.rs @@ -42,7 +42,7 @@ use self::{ string::StringLiteral, template::TemplateLiteral, }; -use crate::syntax::ast::{Position, Punctuator, Span}; +use boa_ast::{Position, Punctuator, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/lexer/number.rs b/boa_engine/src/syntax/lexer/number.rs index 0ad3b4e28c..978bdba364 100644 --- a/boa_engine/src/syntax/lexer/number.rs +++ b/boa_engine/src/syntax/lexer/number.rs @@ -1,12 +1,10 @@ //! This module implements lexing for number literals (123, 787) used in the JavaScript programing language. use crate::{ - syntax::{ - ast::{Position, Span}, - lexer::{token::Numeric, Cursor, Error, Token, TokenKind, Tokenizer}, - }, + syntax::lexer::{token::Numeric, Cursor, Error, Token, TokenKind, Tokenizer}, JsBigInt, }; +use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use num_bigint::BigInt; diff --git a/boa_engine/src/syntax/lexer/operator.rs b/boa_engine/src/syntax/lexer/operator.rs index f53d85bfb7..122dfd0dbd 100644 --- a/boa_engine/src/syntax/lexer/operator.rs +++ b/boa_engine/src/syntax/lexer/operator.rs @@ -1,9 +1,7 @@ //! This module implements lexing for operators (+, - etc.) used in the JavaScript programing language. -use crate::syntax::{ - ast::{Position, Punctuator, Span}, - lexer::{Cursor, Error, Token, TokenKind, Tokenizer}, -}; +use crate::syntax::lexer::{Cursor, Error, Token, TokenKind, Tokenizer}; +use boa_ast::{Position, Punctuator, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/lexer/private_identifier.rs b/boa_engine/src/syntax/lexer/private_identifier.rs index 867461c8ba..0f3185658e 100644 --- a/boa_engine/src/syntax/lexer/private_identifier.rs +++ b/boa_engine/src/syntax/lexer/private_identifier.rs @@ -1,10 +1,8 @@ //! This module implements lexing for private identifiers (#foo, #myvar, etc.) used in the JavaScript programing language. use super::{identifier::Identifier, Cursor, Error, Tokenizer}; -use crate::syntax::{ - ast::{Position, Span}, - lexer::{Token, TokenKind}, -}; +use crate::syntax::lexer::{Token, TokenKind}; +use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/lexer/regex.rs b/boa_engine/src/syntax/lexer/regex.rs index 0e2e9e0018..f72faafc3f 100644 --- a/boa_engine/src/syntax/lexer/regex.rs +++ b/boa_engine/src/syntax/lexer/regex.rs @@ -1,11 +1,9 @@ //! This module implements lexing for regex literals used in the JavaScript programing language. use super::{Cursor, Error, Span, Tokenizer}; -use crate::syntax::{ - ast::Position, - lexer::{Token, TokenKind}, -}; +use crate::syntax::lexer::{Token, TokenKind}; use bitflags::bitflags; +use boa_ast::Position; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::{ diff --git a/boa_engine/src/syntax/lexer/spread.rs b/boa_engine/src/syntax/lexer/spread.rs index fac7c2d963..fe626ab40a 100644 --- a/boa_engine/src/syntax/lexer/spread.rs +++ b/boa_engine/src/syntax/lexer/spread.rs @@ -1,10 +1,8 @@ //! This module implements lexing for spread (...) literals used in the JavaScript programing language. use super::{Cursor, Error, Tokenizer}; -use crate::syntax::{ - ast::{Position, Punctuator, Span}, - lexer::Token, -}; +use crate::syntax::lexer::Token; +use boa_ast::{Position, Punctuator, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/lexer/string.rs b/boa_engine/src/syntax/lexer/string.rs index ea5e58eec5..32a6ba8e51 100644 --- a/boa_engine/src/syntax/lexer/string.rs +++ b/boa_engine/src/syntax/lexer/string.rs @@ -1,10 +1,8 @@ //! This module implements lexing for string literals used in the JavaScript programing language. use super::{Cursor, Error, Tokenizer}; -use crate::syntax::{ - ast::{Position, Span}, - lexer::{Token, TokenKind}, -}; +use crate::syntax::lexer::{Token, TokenKind}; +use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; use std::{ diff --git a/boa_engine/src/syntax/lexer/template.rs b/boa_engine/src/syntax/lexer/template.rs index 68d9df180f..32e7ec6469 100644 --- a/boa_engine/src/syntax/lexer/template.rs +++ b/boa_engine/src/syntax/lexer/template.rs @@ -1,21 +1,16 @@ //! This module implements lexing for template literals used in the JavaScript programing language. use super::{Cursor, Error, Tokenizer}; -use crate::{ - syntax::lexer::string::{StringLiteral, UTF16CodeUnitsBuffer}, - syntax::{ - ast::{Position, Span}, - lexer::{Token, TokenKind}, - }, +use crate::syntax::lexer::{ + string::{StringLiteral, UTF16CodeUnitsBuffer}, + Token, TokenKind, }; +use boa_ast::{Position, Span}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::{self, ErrorKind, Read}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TemplateString { /// The template string of template literal with argument `raw` true. diff --git a/boa_engine/src/syntax/lexer/tests.rs b/boa_engine/src/syntax/lexer/tests.rs index 12bc197e46..ac0d3d51d1 100644 --- a/boa_engine/src/syntax/lexer/tests.rs +++ b/boa_engine/src/syntax/lexer/tests.rs @@ -1,16 +1,12 @@ //! Tests for the lexer. #![allow(clippy::indexing_slicing)] +use boa_ast::Keyword; use boa_interner::Sym; // use super::regex::RegExpFlags; -use super::token::Numeric; -use super::*; -use super::{Error, Position}; -use crate::{ - string::utf16, - syntax::{ast::Keyword, lexer::template::TemplateString}, -}; +use super::{token::Numeric, Error, Position, *}; +use crate::{string::utf16, syntax::lexer::template::TemplateString}; use std::str; fn span(start: (u32, u32), end: (u32, u32)) -> Span { diff --git a/boa_engine/src/syntax/lexer/token.rs b/boa_engine/src/syntax/lexer/token.rs index ae836db3f5..6094fb38be 100644 --- a/boa_engine/src/syntax/lexer/token.rs +++ b/boa_engine/src/syntax/lexer/token.rs @@ -5,14 +5,10 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-tokens -use crate::syntax::{ - ast::{Keyword, Punctuator, Span}, - lexer::template::TemplateString, -}; +use crate::syntax::lexer::template::TemplateString; +use boa_ast::{Keyword, Punctuator, Span}; use boa_interner::{Interner, Sym}; use num_bigint::BigInt; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; /// This represents the smallest individual words, phrases, or characters that JavaScript can understand. /// @@ -20,7 +16,7 @@ use serde::{Deserialize, Serialize}; /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-tokens -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq)] pub struct Token { /// The token kind, which contains the actual data of the token. @@ -55,7 +51,7 @@ impl Token { } /// Represents the type different types of numeric literals. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, PartialEq, Debug)] pub enum Numeric { /// A floating point number @@ -90,7 +86,7 @@ impl From for Numeric { } /// Represents the type of Token and the data it has inside. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, PartialEq, Debug)] pub enum TokenKind { /// A boolean literal, which is either `true` or `false`. diff --git a/boa_engine/src/syntax/mod.rs b/boa_engine/src/syntax/mod.rs index fca8f72b28..7ced1e404c 100644 --- a/boa_engine/src/syntax/mod.rs +++ b/boa_engine/src/syntax/mod.rs @@ -1,7 +1,6 @@ -//! Syntactical analysis, such as Abstract Syntax Tree (AST), Parsing and Lexing +//! Syntactical analysis, such as Parsing and Lexing. // syntax module has a lot of acronyms -pub mod ast; pub mod lexer; pub mod parser; diff --git a/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs b/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs index 88a7fd8925..df34a0b960 100644 --- a/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs +++ b/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs @@ -1,8 +1,8 @@ use crate::syntax::{ - ast::Position, lexer::{InputElement, Lexer, Token, TokenKind}, parser::error::ParseError, }; +use boa_ast::Position; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/cursor/mod.rs b/boa_engine/src/syntax/parser/cursor/mod.rs index 85bfb67e83..1eada2e5e6 100644 --- a/boa_engine/src/syntax/parser/cursor/mod.rs +++ b/boa_engine/src/syntax/parser/cursor/mod.rs @@ -2,10 +2,8 @@ mod buffered_lexer; use super::{statement::PrivateElement, ParseError}; -use crate::syntax::{ - ast::{Position, Punctuator}, - lexer::{InputElement, Lexer, Token, TokenKind}, -}; +use crate::syntax::lexer::{InputElement, Lexer, Token, TokenKind}; +use boa_ast::{Position, Punctuator}; use boa_interner::{Interner, Sym}; use buffered_lexer::BufferedLexer; use rustc_hash::FxHashMap; diff --git a/boa_engine/src/syntax/parser/error.rs b/boa_engine/src/syntax/parser/error.rs index 4f375dcdd1..f0ccb28bdc 100644 --- a/boa_engine/src/syntax/parser/error.rs +++ b/boa_engine/src/syntax/parser/error.rs @@ -1,9 +1,8 @@ //! Error and result implementation for the parser. -use crate::syntax::{ - ast::{Position, Span}, - lexer::Error as LexError, -}; +use crate::syntax::lexer::Error as LexError; + +use boa_ast::{Position, Span}; use std::fmt; /// Result of a parsing operation. 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 145b8628bd..7ec4bd99c6 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs @@ -9,23 +9,23 @@ use super::AssignmentExpression; use crate::syntax::{ - ast::{ - self, - declaration::Variable, - expression::Identifier, - function::{FormalParameter, FormalParameterList, FormalParameterListFlags}, - statement::Return, - Expression, Punctuator, StatementList, - }, lexer::{Error as LexError, TokenKind}, parser::{ error::{ErrorContext, ParseError, ParseResult}, expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - AllowAwait, AllowIn, AllowYield, Cursor, TokenParser, + name_in_lexically_declared_names, AllowAwait, AllowIn, AllowYield, Cursor, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_ast::{ + self as ast, + declaration::Variable, + expression::Identifier, + function::{FormalParameter, FormalParameterList}, + statement::Return, + Expression, Punctuator, StatementList, +}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -78,40 +78,33 @@ where let _timer = Profiler::global().start_event("ArrowFunction", "Parsing"); let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; - let (params, params_start_position) = - if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind() { - // CoverParenthesizedExpressionAndArrowParameterList - let params_start_position = cursor - .expect(Punctuator::OpenParen, "arrow function", interner)? - .span() - .end(); - - let params = FormalParameters::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - cursor.expect(Punctuator::CloseParen, "arrow function", interner)?; - (params, params_start_position) - } else { - let params_start_position = next_token.span().start(); - let param = BindingIdentifier::new(self.allow_yield, self.allow_await) - .parse(cursor, interner) - .context("arrow function")?; - let has_arguments = param == Sym::ARGUMENTS; - let mut flags = FormalParameterListFlags::IS_SIMPLE; - if has_arguments { - flags |= FormalParameterListFlags::HAS_ARGUMENTS; - } - ( - FormalParameterList::new( - Box::new([FormalParameter::new( - Variable::from_identifier(param, None), - false, - )]), - flags, - 1, - ), - params_start_position, - ) - }; + let (params, params_start_position) = if let TokenKind::Punctuator(Punctuator::OpenParen) = + &next_token.kind() + { + // CoverParenthesizedExpressionAndArrowParameterList + let params_start_position = cursor + .expect(Punctuator::OpenParen, "arrow function", interner)? + .span() + .end(); + + let params = FormalParameters::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)?; + cursor.expect(Punctuator::CloseParen, "arrow function", interner)?; + (params, params_start_position) + } else { + let params_start_position = next_token.span().start(); + let param = BindingIdentifier::new(self.allow_yield, self.allow_await) + .parse(cursor, interner) + .context("arrow function")?; + ( + FormalParameterList::try_from(FormalParameter::new( + Variable::from_identifier(param, None), + false, + )) + .expect("a single binding identifier without init is always a valid param list"), + params_start_position, + ) + }; cursor.peek_expect_no_lineterminator(0, "arrow function", interner)?; @@ -144,7 +137,8 @@ where // It is a Syntax Error if any element of the BoundNames of ArrowParameters // also occurs in the LexicallyDeclaredNames of ConciseBody. // https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; diff --git a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs index 064b79b283..9370ad4d35 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs @@ -8,16 +8,16 @@ //! [spec]: https://tc39.es/ecma262/#sec-conditional-operator use crate::syntax::{ - ast::{ - expression::{operator::Conditional, Identifier}, - Expression, Punctuator, - }, lexer::TokenKind, parser::{ expression::{AssignmentExpression, ShortCircuitExpression}, AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{operator::Conditional, Identifier}, + Expression, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs index 6238423428..50a2ab5664 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs @@ -9,19 +9,19 @@ use super::ParseError; use crate::syntax::{ - ast::{ - expression::{ - operator::{binary::ArithmeticOp, Binary}, - Identifier, - }, - Expression, Keyword, Punctuator, - }, lexer::TokenKind, parser::{ expression::{unary::UnaryExpression, update::UpdateExpression}, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{ + operator::{binary::ArithmeticOp, Binary}, + Identifier, + }, + Expression, Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/assignment/mod.rs b/boa_engine/src/syntax/parser/expression/assignment/mod.rs index c9f968cd6f..f7c8e02c05 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/mod.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/mod.rs @@ -13,14 +13,6 @@ mod exponentiation; mod r#yield; use crate::syntax::{ - ast::{ - self, - expression::{ - operator::assign::{Assign, AssignOp, AssignTarget}, - Identifier, - }, - Expression, Keyword, Punctuator, - }, lexer::{Error as LexError, InputElement, TokenKind}, parser::{ expression::assignment::{ @@ -28,15 +20,26 @@ use crate::syntax::{ conditional::ConditionalExpression, r#yield::YieldExpression, }, - AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + name_in_lexically_declared_names, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, + ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + expression::{ + operator::assign::{Assign, AssignOp, AssignTarget}, + Identifier, + }, + Expression, Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; pub(super) use exponentiation::ExponentiationExpression; +use super::check_strict_arguments_or_eval; + /// Assignment expression parsing. /// /// This can be one of the following: @@ -188,7 +191,8 @@ where // It is a Syntax Error if any element of the BoundNames of ArrowParameters // also occurs in the LexicallyDeclaredNames of ConciseBody. // https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors - parameters.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶meters, &body.lexically_declared_names_top_level(), position, )?; @@ -202,7 +206,7 @@ where TokenKind::Punctuator(Punctuator::Assign) => { if cursor.strict_mode() { if let Expression::Identifier(ident) = lhs { - ident.check_strict_arguments_or_eval(position)?; + check_strict_arguments_or_eval(ident, position)?; } } @@ -227,7 +231,7 @@ where TokenKind::Punctuator(p) if p.as_assign_op().is_some() => { if cursor.strict_mode() { if let Expression::Identifier(ident) = lhs { - ident.check_strict_arguments_or_eval(position)?; + check_strict_arguments_or_eval(ident, position)?; } } diff --git a/boa_engine/src/syntax/parser/expression/assignment/yield.rs b/boa_engine/src/syntax/parser/expression/assignment/yield.rs index 0cb033756c..189b314c4a 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/yield.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/yield.rs @@ -9,10 +9,10 @@ use super::AssignmentExpression; use crate::syntax::{ - ast::{expression::Yield, Expression, Keyword, Punctuator}, lexer::TokenKind, parser::{AllowAwait, AllowIn, Cursor, ParseError, ParseResult, TokenParser}, }; +use boa_ast::{expression::Yield, Expression, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/await_expr.rs b/boa_engine/src/syntax/parser/expression/await_expr.rs index 2042685b31..01a83775b7 100644 --- a/boa_engine/src/syntax/parser/expression/await_expr.rs +++ b/boa_engine/src/syntax/parser/expression/await_expr.rs @@ -9,10 +9,10 @@ use super::unary::UnaryExpression; use crate::syntax::{ - ast::{expression::Await, Keyword}, lexer::TokenKind, parser::{AllowYield, Cursor, ParseResult, TokenParser}, }; +use boa_ast::{expression::Await, Keyword}; use boa_interner::Interner; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/identifiers.rs b/boa_engine/src/syntax/parser/expression/identifiers.rs index 2e284dbd3b..c108575af1 100644 --- a/boa_engine/src/syntax/parser/expression/identifiers.rs +++ b/boa_engine/src/syntax/parser/expression/identifiers.rs @@ -6,10 +6,10 @@ //! [spec]: https://tc39.es/ecma262/#sec-identifiers use crate::syntax::{ - ast::{expression::Identifier, Keyword}, lexer::{Error as LexError, TokenKind}, parser::{cursor::Cursor, AllowAwait, AllowYield, ParseError, ParseResult, TokenParser}, }; +use boa_ast::{expression::Identifier, Keyword}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs index b6547c92c5..c98a3a4a7e 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs @@ -8,13 +8,13 @@ //! [spec]: https://tc39.es/ecma262/#prod-Arguments use crate::syntax::{ - ast::{expression::Spread, Expression, Punctuator}, lexer::{InputElement, TokenKind}, parser::{ expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{expression::Spread, Expression, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs index 4528737807..c082a3650e 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs @@ -9,20 +9,20 @@ use super::arguments::Arguments; use crate::syntax::{ - ast::{ - self, - expression::{ - access::{PrivatePropertyAccess, SimplePropertyAccess}, - Call, - }, - Punctuator, - }, lexer::TokenKind, parser::{ expression::{left_hand_side::template::TaggedTemplateLiteral, Expression}, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + expression::{ + access::{PrivatePropertyAccess, SimplePropertyAccess}, + Call, + }, + Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs index 89ad3c7f07..1932961401 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs @@ -7,17 +7,6 @@ use super::arguments::Arguments; use crate::syntax::{ - ast::{ - self, - expression::{ - access::{ - PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess, - SuperPropertyAccess, - }, - Call, Identifier, New, - }, - Keyword, Punctuator, - }, lexer::{InputElement, TokenKind}, parser::{ expression::{ @@ -26,6 +15,16 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + expression::{ + access::{ + PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess, SuperPropertyAccess, + }, + Call, Identifier, New, + }, + Keyword, Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs index 5d6389b2ea..8045fc082f 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs @@ -17,10 +17,6 @@ mod optional; mod template; use crate::syntax::{ - ast::{ - expression::{Identifier, SuperCall}, - Expression, Keyword, Punctuator, - }, lexer::{InputElement, TokenKind}, parser::{ expression::left_hand_side::{ @@ -30,6 +26,10 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{Identifier, SuperCall}, + Expression, Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs index ce78197969..b985c92a95 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs @@ -7,13 +7,6 @@ use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use crate::syntax::{ - ast::{ - self, - expression::{ - access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind, - }, - Punctuator, - }, lexer::{Token, TokenKind}, parser::{ cursor::Cursor, expression::Expression, AllowAwait, AllowYield, ParseError, ParseResult, @@ -22,6 +15,11 @@ use crate::syntax::{ }; use super::arguments::Arguments; +use boa_ast::{ + self as ast, + expression::{access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind}, + Punctuator, +}; /// Parses an optional expression. /// diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs index b7a5988808..bd044368f9 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs @@ -1,15 +1,13 @@ use boa_interner::Interner; use boa_macros::utf16; -use crate::syntax::{ - ast::{ - expression::{ - access::PropertyAccessField, literal::Literal, Identifier, Optional, OptionalOperation, - OptionalOperationKind, - }, - Expression, Statement, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + expression::{ + access::PropertyAccessField, literal::Literal, Identifier, Optional, OptionalOperation, + OptionalOperationKind, }, - parser::tests::{check_invalid, check_parser}, + Expression, Statement, }; #[test] diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs index d8b3518353..16c15e4e49 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs @@ -1,11 +1,11 @@ use crate::syntax::{ - ast::{self, expression::TaggedTemplate, Position, Punctuator}, lexer::TokenKind, parser::{ cursor::Cursor, expression::Expression, AllowAwait, AllowYield, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{self as ast, expression::TaggedTemplate, Position, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs index 50df92d5be..17ba27aecc 100644 --- a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs +++ b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs @@ -1,9 +1,7 @@ -use crate::syntax::{ - ast::{ - expression::{access::SimplePropertyAccess, Call, Identifier}, - Expression, Statement, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::{access::SimplePropertyAccess, Call, Identifier}, + Expression, Statement, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/mod.rs b/boa_engine/src/syntax/parser/expression/mod.rs index fbd3475d88..b4b601ef6d 100644 --- a/boa_engine/src/syntax/parser/expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/mod.rs @@ -20,29 +20,29 @@ pub(in crate::syntax::parser) mod await_expr; mod tests; use crate::syntax::{ - ast::{ - self, - expression::{ - operator::{ - binary::{BinaryOp, LogicalOp}, - Binary, - }, - Identifier, - }, - Keyword, Punctuator, - }, lexer::{InputElement, TokenKind}, parser::{ expression::assignment::ExponentiationExpression, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; -use boa_interner::Interner; + +use boa_ast::{ + self as ast, + expression::{ + operator::{ + binary::{BinaryOp, LogicalOp}, + Binary, + }, + Identifier, + }, + Keyword, Position, Punctuator, +}; +use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; pub(super) use self::{assignment::AssignmentExpression, primary::Initializer}; -pub(in crate::syntax) use identifiers::RESERVED_IDENTIFIERS_STRICT; pub(in crate::syntax::parser) use { identifiers::{BindingIdentifier, LabelIdentifier}, left_hand_side::LeftHandSideExpression, @@ -51,22 +51,6 @@ pub(in crate::syntax::parser) use { }, }; -// For use in the expression! macro to allow for both Punctuator and Keyword parameters. -// Always returns false. -impl PartialEq for Punctuator { - fn eq(&self, _other: &Keyword) -> bool { - false - } -} - -// For use in the expression! macro to allow for both Punctuator and Keyword parameters. -// Always returns false. -impl PartialEq for Keyword { - fn eq(&self, _other: &Punctuator) -> bool { - false - } -} - /// Generates an expression parser for a number of expressions whose production rules are of the following pattern. /// /// ```text @@ -84,48 +68,42 @@ impl PartialEq for Keyword { /// A list of punctuators (operands between the and ) are passed as the third parameter. /// /// The fifth parameter is an Option which sets the goal symbol to set before parsing (or None to leave it as is). -macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => { - impl TokenParser for $name - where - R: Read - { - type Output = ast::Expression; - - fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult { - let _timer = Profiler::global().start_event(stringify!($name), "Parsing"); - - if $goal.is_some() { - cursor.set_goal($goal.unwrap()); - } +macro_rules! expression { + ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => { + impl TokenParser for $name + where + R: Read + { + type Output = ast::Expression; + + fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult { + let _timer = Profiler::global().start_event(stringify!($name), "Parsing"); + + if $goal.is_some() { + cursor.set_goal($goal.unwrap()); + } - let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?; - self.name = None; - while let Some(tok) = cursor.peek(0, interner)? { - match *tok.kind() { - TokenKind::Punctuator(op) if $( op == $op )||* => { - let _next = cursor.next(interner).expect("token disappeared"); - lhs = Binary::new( - op.as_binary_op().expect("Could not get binary operation."), - lhs, - $lower::new($( self.$low_param ),*).parse(cursor, interner)? - ).into(); - } - TokenKind::Keyword((op, false)) if $( op == $op )||* => { - let _next = cursor.next(interner).expect("token disappeared"); - lhs = Binary::new( - op.as_binary_op().expect("Could not get binary operation."), - lhs, - $lower::new($( self.$low_param ),*).parse(cursor, interner)? - ).into(); + let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?; + self.name = None; + while let Some(tok) = cursor.peek(0, interner)? { + match *tok.kind() { + TokenKind::Punctuator(op) if $( op == $op )||* => { + let _next = cursor.next(interner).expect("token disappeared"); + lhs = Binary::new( + op.as_binary_op().expect("Could not get binary operation."), + lhs, + $lower::new($( self.$low_param ),*).parse(cursor, interner)? + ).into(); + } + _ => break } - _ => break } - } - Ok(lhs) + Ok(lhs) + } } - } -} } + }; +} /// Expression parsing. /// @@ -762,3 +740,18 @@ expression!( [name, allow_yield, allow_await], Some(InputElement::Div) ); + +/// Returns an error if `arguments` or `eval` are used as identifier in strict mode. +fn check_strict_arguments_or_eval(ident: Identifier, position: Position) -> Result<(), ParseError> { + match ident.sym() { + Sym::ARGUMENTS => Err(ParseError::general( + "unexpected identifier 'arguments' in strict mode", + position, + )), + Sym::EVAL => Err(ParseError::general( + "unexpected identifier 'eval' in strict mode", + position, + )), + _ => Ok(()), + } +} diff --git a/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs index 5d1050b4d2..e5fe76d974 100644 --- a/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs @@ -11,16 +11,16 @@ mod tests; use crate::syntax::{ - ast::{ - expression::{literal, Spread}, - Punctuator, - }, lexer::TokenKind, parser::{ expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{literal, Spread}, + Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs b/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs index d61d062747..f7dc087b29 100644 --- a/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs @@ -1,11 +1,9 @@ // ! Tests for array initializer parsing. -use crate::syntax::{ - ast::{ - expression::literal::{ArrayLiteral, Literal}, - Expression, Statement, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::literal::{ArrayLiteral, Literal}, + Expression, Statement, }; use boa_interner::{Interner, Sym}; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs index 2e535436f0..8074385773 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs @@ -2,17 +2,15 @@ mod tests; use crate::syntax::{ - ast::{ - expression::Identifier, function::function_contains_super, function::AsyncFunction, - Keyword, Position, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - AllowYield, Cursor, ParseError, ParseResult, TokenParser, + function_contains_super, name_in_lexically_declared_names, AllowYield, Cursor, ParseError, + ParseResult, TokenParser, }, }; +use boa_ast::{expression::Identifier, function::AsyncFunction, Keyword, Position, Punctuator}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -126,7 +124,8 @@ where // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; 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 069d803ea8..cd11c496d0 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 @@ -1,12 +1,10 @@ -use crate::syntax::{ - ast::{ - declaration::{Declaration, LexicalDeclaration, Variable}, - expression::literal::Literal, - function::{AsyncFunction, FormalParameterList}, - statement::Return, - Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + declaration::{Declaration, LexicalDeclaration, Variable}, + expression::literal::Literal, + function::{AsyncFunction, FormalParameterList}, + statement::Return, + Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs index 9f61be7edd..369c37cedf 100644 --- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs @@ -11,17 +11,15 @@ mod tests; use crate::syntax::{ - ast::{ - expression::Identifier, function::function_contains_super, function::AsyncGenerator, - Keyword, Position, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, ParseResult, TokenParser, + function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, + TokenParser, }, }; +use boa_ast::{expression::Identifier, function::AsyncGenerator, Keyword, Position, Punctuator}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -158,7 +156,8 @@ where // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; 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 57c3a17de2..e275bee897 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 @@ -1,14 +1,12 @@ use std::convert::TryInto; -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::literal::Literal, - function::{AsyncGenerator, FormalParameterList}, - statement::Return, - Declaration, Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::literal::Literal, + function::{AsyncGenerator, FormalParameterList}, + statement::Return, + Declaration, Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs index fba2483f95..dc41a142b5 100644 --- a/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs @@ -1,11 +1,11 @@ use crate::syntax::{ - ast::{expression::Identifier, function::Class, Keyword}, lexer::TokenKind, parser::{ expression::BindingIdentifier, statement::ClassTail, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{expression::Identifier, function::Class, Keyword}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs index 03dcef6082..65b0459409 100644 --- a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs @@ -11,18 +11,15 @@ mod tests; use crate::syntax::{ - ast::{ - expression::Identifier, - function::{function_contains_super, Function}, - Keyword, Position, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, ParseResult, TokenParser, + function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, + TokenParser, }, }; +use boa_ast::{expression::Identifier, function::Function, Keyword, Position, Punctuator}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -121,7 +118,8 @@ where // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; 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 1f273b75e4..cf63d1735c 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 @@ -1,12 +1,10 @@ -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::literal::Literal, - function::{FormalParameterList, Function}, - statement::Return, - Declaration, Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::literal::Literal, + function::{FormalParameterList, Function}, + statement::Return, + Declaration, Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs index 24ae0cb4c2..85c3306d72 100644 --- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs @@ -11,18 +11,15 @@ mod tests; use crate::syntax::{ - ast::{ - expression::Identifier, - function::{function_contains_super, Generator}, - Position, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, ParseResult, TokenParser, + function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult, + TokenParser, }, }; +use boa_ast::{expression::Identifier, function::Generator, Position, Punctuator}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -123,7 +120,8 @@ where // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs index 2fb521e60e..819dafcb3a 100644 --- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs @@ -1,11 +1,9 @@ -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::{literal::Literal, Yield}, - function::{FormalParameterList, Generator}, - Declaration, Expression, Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::{literal::Literal, Yield}, + function::{FormalParameterList, Generator}, + Declaration, Expression, Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs index b160d25c74..6c61b73ace 100644 --- a/boa_engine/src/syntax/parser/expression/primary/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs @@ -27,24 +27,6 @@ use self::{ object_initializer::ObjectLiteral, }; use crate::syntax::{ - ast::{ - self, - declaration::Variable, - expression::{ - literal::Literal, - operator::{ - assign::{ - array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, - AssignTarget, - }, - binary::BinaryOp, - }, - Call, Identifier, New, - }, - function::{FormalParameter, FormalParameterList}, - pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, - Keyword, Punctuator, Span, - }, lexer::{token::Numeric, InputElement, Token, TokenKind}, parser::{ expression::{ @@ -55,6 +37,18 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + declaration::Variable, + expression::{ + literal::Literal, + operator::{assign::AssignTarget, binary::BinaryOp}, + Call, Identifier, New, + }, + function::{FormalParameter, FormalParameterList}, + pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, + Keyword, Punctuator, Span, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -549,34 +543,30 @@ fn expression_to_formal_parameters( } }, ast::Expression::ObjectLiteral(object) => { - let decl = object_decl_to_declaration_pattern(object, strict); - - if let Some(pattern) = decl { - parameters.push(FormalParameter::new( - Variable::from_pattern(pattern.into(), None), - false, - )); - } else { - return Err(ParseError::general( + let pattern = object.to_pattern(strict).ok_or_else(|| { + ParseError::general( "invalid object binding pattern in formal parameter list", span.start(), - )); - } + ) + })?; + + parameters.push(FormalParameter::new( + Variable::from_pattern(pattern.into(), None), + false, + )); } ast::Expression::ArrayLiteral(array) => { - let decl = array_decl_to_declaration_pattern(array, strict); - - if let Some(pattern) = decl { - parameters.push(FormalParameter::new( - Variable::from_pattern(pattern.into(), None), - false, - )); - } else { - return Err(ParseError::general( + let pattern = array.to_pattern(strict).ok_or_else(|| { + ParseError::general( "invalid array binding pattern in formal parameter list", span.start(), - )); - } + ) + })?; + + parameters.push(FormalParameter::new( + Variable::from_pattern(pattern.into(), None), + false, + )); } _ => { return Err(ParseError::unexpected( diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs index 26d2f7414b..03e75fd4b6 100644 --- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -11,22 +11,22 @@ mod tests; use crate::syntax::{ - ast::{ - expression::{ - literal::{self, Literal}, - Identifier, - }, - function::{function_contains_super, has_direct_super}, - function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, - property::{self, MethodDefinition}, - Expression, Keyword, Punctuator, - }, lexer::{token::Numeric, Error as LexError, TokenKind}, parser::{ expression::{identifiers::IdentifierReference, AssignmentExpression}, function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters}, - AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + function_contains_super, has_direct_super, name_in_lexically_declared_names, AllowAwait, + AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + }, +}; +use boa_ast::{ + expression::{ + literal::{self, Literal}, + Identifier, }, + function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, + property::{self, MethodDefinition}, + Expression, Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; use boa_macros::utf16; @@ -445,7 +445,7 @@ where // It is a Syntax Error if any element of the BoundNames of FormalParameters also occurs in the LexicallyDeclaredNames of FunctionBody. let lexically_declared_names = body.lexically_declared_names(); - for parameter in params.parameters.iter() { + for parameter in params.as_ref() { for name in ¶meter.names() { if lexically_declared_names.contains(&(*name, false)) { return Err(ParseError::general( @@ -717,7 +717,8 @@ where // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also // occurs in the LexicallyDeclaredNames of GeneratorBody. - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), body_start, )?; @@ -831,7 +832,8 @@ where // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also // occurs in the LexicallyDeclaredNames of GeneratorBody. - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), body_start, )?; @@ -916,7 +918,8 @@ where // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also // occurs in the LexicallyDeclaredNames of GeneratorBody. - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), body_start, )?; diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs index 898f43aeb7..37b0c4738a 100644 --- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs @@ -1,18 +1,16 @@ -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::{ - literal::{Literal, ObjectLiteral}, - Identifier, - }, - function::{ - AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList, - FormalParameterListFlags, Function, - }, - property::{MethodDefinition, PropertyDefinition, PropertyName}, - Declaration, StatementList, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::{ + literal::{Literal, ObjectLiteral}, + Identifier, }, - parser::tests::{check_invalid, check_parser}, + function::{ + AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList, + FormalParameterListFlags, Function, + }, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; @@ -96,6 +94,17 @@ fn check_object_short_function() { fn check_object_short_function_arguments() { let mut interner = Interner::default(); + let parameters = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier( + interner.get_or_intern_static("test", utf16!("test")).into(), + None, + ), + false, + )); + + assert_eq!(parameters.flags(), FormalParameterListFlags::default()); + assert_eq!(parameters.length(), 1); + let object_properties = vec![ PropertyDefinition::Property( interner.get_or_intern_static("a", utf16!("a")).into(), @@ -103,21 +112,7 @@ fn check_object_short_function_arguments() { ), PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), - MethodDefinition::Ordinary(Function::new( - None, - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("test", utf16!("test")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, - StatementList::default(), - )), + MethodDefinition::Ordinary(Function::new(None, parameters, StatementList::default())), ), ]; @@ -182,6 +177,17 @@ fn check_object_getter() { fn check_object_setter() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier( + interner.get_or_intern_static("test", utf16!("test")).into(), + None, + ), + false, + )); + + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); + let object_properties = vec![ PropertyDefinition::Property( interner.get_or_intern_static("a", utf16!("a")).into(), @@ -189,21 +195,7 @@ fn check_object_setter() { ), PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), - MethodDefinition::Set(Function::new( - None, - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("test", utf16!("test")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, - StatementList::default(), - )), + MethodDefinition::Set(Function::new(None, params, StatementList::default())), ), ]; diff --git a/boa_engine/src/syntax/parser/expression/primary/template/mod.rs b/boa_engine/src/syntax/parser/expression/primary/template/mod.rs index 807faad5cd..187b088017 100644 --- a/boa_engine/src/syntax/parser/expression/primary/template/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/template/mod.rs @@ -8,16 +8,16 @@ //! [spec]: https://tc39.es/ecma262/#sec-template-literals use crate::syntax::{ - ast::{ - expression::literal::{self, TemplateElement}, - Position, Punctuator, - }, lexer::TokenKind, parser::{ expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::literal::{self, TemplateElement}, + Position, Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/primary/tests.rs b/boa_engine/src/syntax/parser/expression/primary/tests.rs index c793eb79ae..0097f032a0 100644 --- a/boa_engine/src/syntax/parser/expression/primary/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/tests.rs @@ -1,7 +1,5 @@ -use crate::syntax::{ - ast::{expression::literal::Literal, Expression, Statement}, - parser::tests::check_parser, -}; +use crate::syntax::parser::tests::check_parser; +use boa_ast::{expression::literal::Literal, Expression, Statement}; use boa_interner::{Interner, Sym}; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/tests.rs b/boa_engine/src/syntax/parser/expression/tests.rs index 3356624ef0..21066e8459 100644 --- a/boa_engine/src/syntax/parser/expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/tests.rs @@ -1,18 +1,16 @@ -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::{ - literal::Literal, - operator::{ - assign::AssignOp, - binary::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp}, - Assign, Binary, - }, - Call, Identifier, New, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::{ + literal::Literal, + operator::{ + assign::AssignOp, + binary::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp}, + Assign, Binary, }, - Declaration, Expression, Statement, + Call, Identifier, New, }, - parser::tests::{check_invalid, check_parser}, + Declaration, Expression, Statement, }; use boa_interner::{Interner, Sym}; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/expression/unary.rs b/boa_engine/src/syntax/parser/expression/unary.rs index 241c15d764..9593ccbb64 100644 --- a/boa_engine/src/syntax/parser/expression/unary.rs +++ b/boa_engine/src/syntax/parser/expression/unary.rs @@ -8,20 +8,20 @@ //! [spec]: https://tc39.es/ecma262/#sec-unary-operators use crate::syntax::{ - ast::{ - expression::{ - access::PropertyAccess, - operator::{unary::UnaryOp, Unary}, - Identifier, - }, - Expression, Keyword, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::{await_expr::AwaitExpression, update::UpdateExpression}, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{ + access::PropertyAccess, + operator::{unary::UnaryOp, Unary}, + Identifier, + }, + Expression, Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/expression/update.rs b/boa_engine/src/syntax/parser/expression/update.rs index bd0af16cd8..386b50b766 100644 --- a/boa_engine/src/syntax/parser/expression/update.rs +++ b/boa_engine/src/syntax/parser/expression/update.rs @@ -5,21 +5,21 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-update-expressions -use super::left_hand_side::LeftHandSideExpression; +use super::{check_strict_arguments_or_eval, left_hand_side::LeftHandSideExpression}; use crate::syntax::{ - ast::{ - expression::{ - operator::{unary::UnaryOp, Unary}, - Identifier, - }, - Expression, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::unary::UnaryExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::{ + operator::{unary::UnaryOp, Unary}, + Identifier, + }, + Expression, Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -75,7 +75,7 @@ where if cursor.strict_mode() { if let Expression::Identifier(ident) = target { - ident.check_strict_arguments_or_eval(position)?; + check_strict_arguments_or_eval(ident, position)?; } } @@ -91,7 +91,7 @@ where if cursor.strict_mode() { if let Expression::Identifier(ident) = target { - ident.check_strict_arguments_or_eval(position)?; + check_strict_arguments_or_eval(ident, position)?; } } diff --git a/boa_engine/src/syntax/parser/function/mod.rs b/boa_engine/src/syntax/parser/function/mod.rs index cf4f57b0f9..5476d76971 100644 --- a/boa_engine/src/syntax/parser/function/mod.rs +++ b/boa_engine/src/syntax/parser/function/mod.rs @@ -11,12 +11,6 @@ mod tests; use crate::syntax::{ - ast::{ - self, - declaration::Variable, - function::{FormalParameterList, FormalParameterListFlags}, - Punctuator, - }, lexer::{Error as LexError, InputElement, TokenKind}, parser::{ expression::{BindingIdentifier, Initializer}, @@ -24,10 +18,15 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseError, TokenParser, }, }; +use boa_ast::{ + self as ast, + declaration::Variable, + function::{FormalParameterList, FormalParameterListFlags}, + Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_macros::utf16; use boa_profiler::Profiler; -use rustc_hash::FxHashSet; use std::io::Read; use super::ParseResult; @@ -70,43 +69,25 @@ where let _timer = Profiler::global().start_event("FormalParameters", "Parsing"); cursor.set_goal(InputElement::RegExp); - let mut flags = FormalParameterListFlags::default(); let mut params = Vec::new(); - let mut length = 0; let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; if next_token.kind() == &TokenKind::Punctuator(Punctuator::CloseParen) { - return Ok(FormalParameterList::new( - params.into_boxed_slice(), - flags, - length, - )); + return Ok(FormalParameterList::default()); } let start_position = next_token.span().start(); - let mut parameter_names = FxHashSet::default(); - loop { let mut rest_param = false; let next_param = match cursor.peek(0, interner)? { Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Spread) => { rest_param = true; - flags |= FormalParameterListFlags::HAS_REST_PARAMETER; FunctionRestParameter::new(self.allow_yield, self.allow_await) .parse(cursor, interner)? } - _ => { - let param = FormalParameter::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?; - if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS) - || param.is_rest_param() - || param.init().is_some()) - { - length += 1; - } - param - } + _ => FormalParameter::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)?, }; if next_param.is_rest_param() && next_param.init().is_some() { @@ -116,25 +97,6 @@ where ))); } - if next_param.init().is_some() { - flags |= FormalParameterListFlags::HAS_EXPRESSIONS; - } - if next_param.names().contains(&Sym::ARGUMENTS.into()) { - flags |= FormalParameterListFlags::HAS_ARGUMENTS; - } - - if next_param.is_rest_param() - || next_param.init().is_some() - || !next_param.is_identifier() - { - flags.remove(FormalParameterListFlags::IS_SIMPLE); - } - for param_name in next_param.names() { - if parameter_names.contains(¶m_name) { - flags |= FormalParameterListFlags::HAS_DUPLICATES; - } - parameter_names.insert(Box::from(param_name)); - } params.push(next_param); if cursor @@ -166,21 +128,21 @@ where } } + let params = FormalParameterList::from_parameters(params); + // Early Error: It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false // and BoundNames of FormalParameterList contains any duplicate elements. - if !flags.contains(FormalParameterListFlags::IS_SIMPLE) - && flags.contains(FormalParameterListFlags::HAS_DUPLICATES) + if !params.flags().contains(FormalParameterListFlags::IS_SIMPLE) + && params + .flags() + .contains(FormalParameterListFlags::HAS_DUPLICATES) { return Err(ParseError::lex(LexError::Syntax( "Duplicate parameter name not allowed in this context".into(), start_position, ))); } - Ok(FormalParameterList::new( - params.into_boxed_slice(), - flags, - length, - )) + Ok(params) } } diff --git a/boa_engine/src/syntax/parser/function/tests.rs b/boa_engine/src/syntax/parser/function/tests.rs index 867abe974c..c4b184356d 100644 --- a/boa_engine/src/syntax/parser/function/tests.rs +++ b/boa_engine/src/syntax/parser/function/tests.rs @@ -1,45 +1,38 @@ use crate::{ string::utf16, - syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::{ - operator::{binary::ArithmeticOp, Binary}, - Identifier, - }, - function::{ - ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, - Function, - }, - statement::Return, - Declaration, Expression, Statement, StatementList, StatementListItem, - }, - parser::tests::check_parser, - Parser, - }, + syntax::{parser::tests::check_parser, Parser}, Context, }; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::{ + operator::{binary::ArithmeticOp, Binary}, + Identifier, + }, + function::{ + ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function, + }, + statement::Return, + Declaration, Expression, Statement, StatementList, StatementListItem, +}; use boa_interner::Interner; /// Checks basic function declaration parsing. #[test] fn check_basic() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); + check_parser( "function foo(a) { return a; }", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(), @@ -56,31 +49,26 @@ fn check_basic() { #[test] fn check_duplicates_strict_off() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + ]); + assert_eq!( + params.flags(), + FormalParameterListFlags::default().union(FormalParameterListFlags::HAS_DUPLICATES) + ); + assert_eq!(params.length(), 2); check_parser( "function foo(a, a) { return a; }", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default() - .union(FormalParameterListFlags::HAS_DUPLICATES), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(), @@ -107,21 +95,18 @@ fn check_duplicates_strict_on() { #[test] fn check_basic_semicolon_insertion() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); + check_parser( "function foo(a) { return a }", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(), @@ -138,21 +123,17 @@ fn check_basic_semicolon_insertion() { #[test] fn check_empty_return() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "function foo(a) { return; }", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(None), ))] @@ -167,21 +148,17 @@ fn check_empty_return() { #[test] fn check_empty_return_semicolon_insertion() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "function foo(a) { return }", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(None), ))] @@ -196,31 +173,26 @@ fn check_empty_return_semicolon_insertion() { #[test] fn check_rest_operator() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + true, + ), + ]); + assert_eq!( + params.flags(), + FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER) + ); + assert_eq!(params.length(), 1); check_parser( "function foo(a, ...b) {}", vec![Declaration::Function(Function::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - true, - ), - ]), - flags: FormalParameterListFlags::empty() - .union(FormalParameterListFlags::HAS_REST_PARAMETER), - length: 1, - }, + params, StatementList::default(), )) .into()], @@ -232,22 +204,20 @@ fn check_rest_operator() { #[test] fn check_arrow_only_rest() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + true, + )); + assert_eq!( + params.flags(), + FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER) + ); + assert_eq!(params.length(), 0); check_parser( "(...a) => {}", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - true, - )]), - flags: FormalParameterListFlags::empty() - .union(FormalParameterListFlags::HAS_REST_PARAMETER), - length: 0, - }, + params, StatementList::default(), ))) .into()], @@ -259,38 +229,30 @@ fn check_arrow_only_rest() { #[test] fn check_arrow_rest() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None), + true, + ), + ]); + assert_eq!( + params.flags(), + FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER) + ); + assert_eq!(params.length(), 2); check_parser( "(a, b, ...c) => {}", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("c", utf16!("c")).into(), - None, - ), - true, - ), - ]), - flags: FormalParameterListFlags::empty() - .union(FormalParameterListFlags::HAS_REST_PARAMETER), - length: 2, - }, + params, StatementList::default(), ))) .into()], @@ -302,30 +264,23 @@ fn check_arrow_rest() { #[test] fn check_arrow() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 2); check_parser( "(a, b) => { return a + b; }", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Binary::new( @@ -347,30 +302,21 @@ fn check_arrow() { #[test] fn check_arrow_semicolon_insertion() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); check_parser( "(a, b) => { return a + b }", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Binary::new( @@ -392,30 +338,21 @@ fn check_arrow_semicolon_insertion() { #[test] fn check_arrow_epty_return() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); check_parser( "(a, b) => { return; }", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(None), ))] @@ -430,30 +367,21 @@ fn check_arrow_epty_return() { #[test] fn check_arrow_empty_return_semicolon_insertion() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); check_parser( "(a, b) => { return }", vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(None), ))] @@ -467,6 +395,12 @@ fn check_arrow_empty_return_semicolon_insertion() { #[test] fn check_arrow_assignment() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "let foo = (a) => { return a };", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -475,17 +409,7 @@ fn check_arrow_assignment() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -508,6 +432,12 @@ fn check_arrow_assignment() { #[test] fn check_arrow_assignment_nobrackets() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "let foo = (a) => a;", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -516,17 +446,7 @@ fn check_arrow_assignment_nobrackets() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -549,6 +469,12 @@ fn check_arrow_assignment_nobrackets() { #[test] fn check_arrow_assignment_noparenthesis() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "let foo = a => { return a };", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -557,17 +483,7 @@ fn check_arrow_assignment_noparenthesis() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -590,6 +506,12 @@ fn check_arrow_assignment_noparenthesis() { #[test] fn check_arrow_assignment_noparenthesis_nobrackets() { let mut interner = Interner::default(); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + )); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 1); check_parser( "let foo = a => a;", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -598,17 +520,7 @@ fn check_arrow_assignment_noparenthesis_nobrackets() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - )]), - flags: FormalParameterListFlags::default(), - length: 1, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -631,6 +543,18 @@ fn check_arrow_assignment_noparenthesis_nobrackets() { #[test] fn check_arrow_assignment_2arg() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 2); check_parser( "let foo = (a, b) => { return a };", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -639,26 +563,7 @@ fn check_arrow_assignment_2arg() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -681,6 +586,18 @@ fn check_arrow_assignment_2arg() { #[test] fn check_arrow_assignment_2arg_nobrackets() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + ]); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 2); check_parser( "let foo = (a, b) => a;", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -689,26 +606,7 @@ fn check_arrow_assignment_2arg_nobrackets() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 2, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -731,6 +629,22 @@ fn check_arrow_assignment_2arg_nobrackets() { #[test] fn check_arrow_assignment_3arg() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None), + false, + ), + ]); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 3); check_parser( "let foo = (a, b, c) => { return a };", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -739,33 +653,7 @@ fn check_arrow_assignment_3arg() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("c", utf16!("c")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 3, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) @@ -788,6 +676,22 @@ fn check_arrow_assignment_3arg() { #[test] fn check_arrow_assignment_3arg_nobrackets() { let mut interner = Interner::default(); + let params = FormalParameterList::from(vec![ + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None), + false, + ), + FormalParameter::new( + Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None), + false, + ), + ]); + assert_eq!(params.flags(), FormalParameterListFlags::default()); + assert_eq!(params.length(), 3); check_parser( "let foo = (a, b, c) => a;", vec![Declaration::Lexical(LexicalDeclaration::Let( @@ -796,33 +700,7 @@ fn check_arrow_assignment_3arg_nobrackets() { Some( ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), - FormalParameterList { - parameters: Box::new([ - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("a", utf16!("a")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("b", utf16!("b")).into(), - None, - ), - false, - ), - FormalParameter::new( - Variable::from_identifier( - interner.get_or_intern_static("c", utf16!("c")).into(), - None, - ), - false, - ), - ]), - flags: FormalParameterListFlags::default(), - length: 3, - }, + params, vec![StatementListItem::Statement(Statement::Return( Return::new(Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs index 1d1bd90559..b78aaa5a6e 100644 --- a/boa_engine/src/syntax/parser/mod.rs +++ b/boa_engine/src/syntax/parser/mod.rs @@ -27,9 +27,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; pub use self::error::{ParseError, ParseResult}; -pub(in crate::syntax) use expression::RESERVED_IDENTIFIERS_STRICT; -use super::ast::{ +use boa_ast::{ expression::Identifier, function::FormalParameterList, ContainsSymbol, Position, StatementList, }; @@ -426,3 +425,60 @@ where Ok(body) } } + +// Checks if a function contains a super call or super property access. +fn function_contains_super(body: &StatementList, parameters: &FormalParameterList) -> bool { + for param in parameters.as_ref() { + if param.variable().contains(ContainsSymbol::SuperCall) + || param.variable().contains(ContainsSymbol::SuperProperty) + { + return true; + } + } + for node in body.statements() { + if node.contains(ContainsSymbol::SuperCall) || node.contains(ContainsSymbol::SuperProperty) + { + return true; + } + } + false +} + +/// Returns `true` if the function parameters or body contain a direct `super` call. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper +pub fn has_direct_super(body: &StatementList, parameters: &FormalParameterList) -> bool { + for param in parameters.as_ref() { + if param.variable().contains(ContainsSymbol::SuperCall) { + return true; + } + } + for node in body.statements() { + if node.contains(ContainsSymbol::SuperCall) { + return true; + } + } + false +} + +/// Helper to check if any parameter names are declared in the given list. +fn name_in_lexically_declared_names( + parameter_list: &FormalParameterList, + names: &[Identifier], + position: Position, +) -> Result<(), ParseError> { + for parameter in parameter_list.as_ref() { + for name in ¶meter.names() { + if names.contains(name) { + return Err(ParseError::General { + message: "formal parameter declared in lexically declared names", + position, + }); + } + } + } + Ok(()) +} diff --git a/boa_engine/src/syntax/parser/statement/block/mod.rs b/boa_engine/src/syntax/parser/statement/block/mod.rs index 73030c642c..4e62c2a2ac 100644 --- a/boa_engine/src/syntax/parser/statement/block/mod.rs +++ b/boa_engine/src/syntax/parser/statement/block/mod.rs @@ -12,10 +12,10 @@ mod tests; use super::StatementList; use crate::syntax::{ - ast::{expression::Identifier, statement, Punctuator}, lexer::TokenKind, parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; +use boa_ast::{expression::Identifier, statement, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use rustc_hash::{FxHashMap, FxHashSet}; diff --git a/boa_engine/src/syntax/parser/statement/block/tests.rs b/boa_engine/src/syntax/parser/statement/block/tests.rs index e38fa4f90e..2be9fb324b 100644 --- a/boa_engine/src/syntax/parser/statement/block/tests.rs +++ b/boa_engine/src/syntax/parser/statement/block/tests.rs @@ -2,19 +2,17 @@ use std::convert::TryInto; -use crate::syntax::{ - ast::{ - declaration::{VarDeclaration, Variable}, - expression::{ - literal::Literal, - operator::{assign::AssignOp, unary::UnaryOp, Assign, Unary}, - Call, Identifier, - }, - function::{FormalParameterList, Function}, - statement::{Block, Return}, - Declaration, Expression, Statement, StatementListItem, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + declaration::{VarDeclaration, Variable}, + expression::{ + literal::Literal, + operator::{assign::AssignOp, unary::UnaryOp, Assign, Unary}, + Call, Identifier, }, - parser::tests::check_parser, + function::{FormalParameterList, Function}, + statement::{Block, Return}, + Declaration, Expression, Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs index 6c766ef0ed..bc523fb215 100644 --- a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs @@ -11,7 +11,6 @@ mod tests; use crate::syntax::{ - ast::{statement::Break, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, @@ -19,6 +18,7 @@ use crate::syntax::{ AllowAwait, AllowYield, ParseResult, TokenParser, }, }; +use boa_ast::{statement::Break, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/break_stm/tests.rs b/boa_engine/src/syntax/parser/statement/break_stm/tests.rs index 824648a82b..38d6f82fd4 100644 --- a/boa_engine/src/syntax/parser/statement/break_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/break_stm/tests.rs @@ -1,10 +1,8 @@ -use crate::syntax::{ - ast::{ - expression::literal::Literal, - statement::{Block, Break, WhileLoop}, - Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::literal::Literal, + statement::{Block, Break, WhileLoop}, + Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs index fc33649954..c81d0a59b5 100644 --- a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs @@ -11,7 +11,6 @@ mod tests; use crate::syntax::{ - ast::{statement::Continue, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, @@ -19,6 +18,7 @@ use crate::syntax::{ AllowAwait, AllowYield, ParseResult, TokenParser, }, }; +use boa_ast::{statement::Continue, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs b/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs index 7f83ae3e07..600646a082 100644 --- a/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs @@ -1,10 +1,8 @@ -use crate::syntax::{ - ast::{ - expression::literal::Literal, - statement::{Block, Continue, WhileLoop}, - Statement, StatementListItem, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::literal::Literal, + statement::{Block, Continue, WhileLoop}, + Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs index 9286aa0229..0d7abe6b6d 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -1,14 +1,11 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::function::AsyncFunction, - ast::Keyword, - parser::{ - statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, - }, +use crate::syntax::parser::{ + statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }; +use boa_ast::{function::AsyncFunction, Keyword}; use boa_interner::Interner; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs index 3a30a2ea49..6b5f8382b0 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs @@ -1,9 +1,7 @@ -use crate::syntax::{ - ast::{ - function::{AsyncFunction, FormalParameterList}, - Declaration, StatementList, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + function::{AsyncFunction, FormalParameterList}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs index 5dd8ef292d..2f5b0b2f4e 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs @@ -6,13 +6,11 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{function::AsyncGenerator, Keyword, Punctuator}, - parser::{ - statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, - }, +use crate::syntax::parser::{ + statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }; +use boa_ast::{function::AsyncGenerator, Keyword, Punctuator}; use boa_interner::Interner; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs index 7a0fd51c16..15ad31e4bf 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs @@ -1,9 +1,7 @@ -use crate::syntax::{ - ast::{ - function::{AsyncGenerator, FormalParameterList}, - Declaration, StatementList, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + function::{AsyncGenerator, FormalParameterList}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs index 15d95617e2..d679ebf94b 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs @@ -2,15 +2,6 @@ mod tests; use crate::syntax::{ - ast::{ - self, - expression::Identifier, - function::{ - self, function_contains_super, has_direct_super, Class, FormalParameterList, Function, - }, - property::{ClassElementName, MethodDefinition}, - ContainsSymbol, Declaration, Expression, Keyword, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::{ @@ -18,10 +9,18 @@ use crate::syntax::{ GeneratorMethod, LeftHandSideExpression, PropertyName, }, function::{FormalParameters, FunctionBody, UniqueFormalParameters, FUNCTION_BREAK_TOKENS}, + function_contains_super, has_direct_super, statement::StatementList, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + expression::Identifier, + function::{self, Class, FormalParameterList, Function}, + property::{ClassElementName, MethodDefinition}, + ContainsSymbol, Declaration, Expression, Keyword, Punctuator, +}; use boa_interner::{Interner, Sym}; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -919,7 +918,7 @@ where let method = MethodDefinition::Get(Function::new( None, - FormalParameterList::empty(), + FormalParameterList::default(), body, )); if r#static { diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs index 7c394688ec..af1b9f95da 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs @@ -1,11 +1,9 @@ -use crate::syntax::{ - ast::{ - expression::literal::Literal, - function::{Class, ClassElement, FormalParameterList, Function}, - property::{MethodDefinition, PropertyName}, - Declaration, StatementList, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::literal::Literal, + function::{Class, ClassElement, FormalParameterList, Function}, + property::{MethodDefinition, PropertyName}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs index 7c237367ef..ec4d11010e 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs @@ -1,13 +1,11 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{function::Function, Keyword}, - parser::{ - statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, - }, +use crate::syntax::parser::{ + statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }; +use boa_ast::{function::Function, Keyword}; use boa_interner::Interner; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs index d8552dca96..6000d0375a 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs @@ -1,9 +1,7 @@ -use crate::syntax::{ - ast::{ - function::{FormalParameterList, Function}, - Declaration, StatementList, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + function::{FormalParameterList, Function}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs index d991cc0cca..8ebdbb14ce 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs @@ -1,14 +1,11 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::function::Generator, - ast::{Keyword, Punctuator}, - parser::{ - statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, - }, +use crate::syntax::parser::{ + statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }; +use boa_ast::{function::Generator, Keyword, Punctuator}; use boa_interner::Interner; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs index 64f2b5bb14..0d568a724e 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs @@ -1,9 +1,7 @@ -use crate::syntax::{ - ast::{ - function::{FormalParameterList, Generator}, - Declaration, StatementList, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + function::{FormalParameterList, Generator}, + Declaration, StatementList, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs index c7ada60691..732c00c338 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -20,19 +20,19 @@ use self::{ class_decl::ClassDeclaration, generator_decl::GeneratorDeclaration, }; use crate::syntax::{ - ast::{ - expression::Identifier, - function::{function_contains_super, FormalParameterList}, - Declaration, Keyword, Position, Punctuator, StatementList, - }, lexer::TokenKind, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, + function_contains_super, name_in_lexically_declared_names, statement::LexError, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + expression::Identifier, function::FormalParameterList, Declaration, Keyword, Position, + Punctuator, StatementList, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -210,7 +210,8 @@ fn parse_callable_declaration( // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - params.name_in_lexically_declared_names( + name_in_lexically_declared_names( + ¶ms, &body.lexically_declared_names_top_level(), params_start_position, )?; diff --git a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs index 994cf2b94f..0f2bfca73f 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs @@ -8,7 +8,6 @@ //! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations use crate::syntax::{ - ast::{self, declaration::Variable, pattern::Pattern, Keyword, Punctuator}, lexer::{Error as LexError, TokenKind}, parser::{ cursor::{Cursor, SemicolonResult}, @@ -17,6 +16,7 @@ use crate::syntax::{ AllowAwait, AllowIn, AllowYield, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{self as ast, declaration::Variable, pattern::Pattern, Keyword, Punctuator}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::{convert::TryInto, io::Read}; diff --git a/boa_engine/src/syntax/parser/statement/declaration/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/mod.rs index d5f3918db0..2aa8052b16 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/mod.rs @@ -19,10 +19,10 @@ use hoistable::HoistableDeclaration; pub(in crate::syntax::parser) use lexical::LexicalDeclaration; use crate::syntax::{ - ast::{self, Keyword}, lexer::TokenKind, parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; +use boa_ast::{self as ast, Keyword}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/declaration/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/tests.rs index ef6fb26081..db6d917220 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/tests.rs @@ -1,12 +1,10 @@ use std::convert::TryInto; -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, VarDeclaration, Variable}, - expression::literal::Literal, - Declaration, Statement, - }, - parser::tests::{check_invalid, check_parser}, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{LexicalDeclaration, VarDeclaration, Variable}, + expression::literal::Literal, + Declaration, Statement, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/expression/mod.rs b/boa_engine/src/syntax/parser/statement/expression/mod.rs index 57f7ee3343..6d9db6ec79 100644 --- a/boa_engine/src/syntax/parser/statement/expression/mod.rs +++ b/boa_engine/src/syntax/parser/statement/expression/mod.rs @@ -1,11 +1,11 @@ use crate::syntax::{ - ast::{Keyword, Punctuator, Statement}, lexer::TokenKind, parser::{ expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{Keyword, Punctuator, Statement}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; 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 79a56b9214..1c06bf2a68 100644 --- a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs @@ -2,16 +2,16 @@ mod tests; use crate::syntax::{ - ast::{ - statement::{Block, If}, - Declaration, Keyword, Punctuator, StatementListItem, - }, lexer::TokenKind, parser::{ expression::Expression, statement::declaration::FunctionDeclaration, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + statement::{Block, If}, + Declaration, Keyword, Punctuator, StatementListItem, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/if_stm/tests.rs b/boa_engine/src/syntax/parser/statement/if_stm/tests.rs index a4a694d7cf..405f229e0f 100644 --- a/boa_engine/src/syntax/parser/statement/if_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/if_stm/tests.rs @@ -1,10 +1,8 @@ -use crate::syntax::{ - ast::{ - expression::literal::Literal, - statement::{Block, If}, - Statement, - }, - parser::tests::check_parser, +use crate::syntax::parser::tests::check_parser; +use boa_ast::{ + expression::literal::Literal, + statement::{Block, If}, + Statement, }; use boa_interner::Interner; 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 655a17b0f0..fd15795967 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 @@ -8,13 +8,13 @@ //! [spec]: https://tc39.es/ecma262/#sec-do-while-statement use crate::syntax::{ - ast::{statement::DoWhileLoop, Keyword, Punctuator}, lexer::TokenKind, parser::{ expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{statement::DoWhileLoop, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; 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 e08232a3a2..146739f747 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs @@ -8,17 +8,6 @@ //! [spec]: https://tc39.es/ecma262/#sec-for-statement use crate::syntax::{ - ast::{ - self, - expression::operator::assign::{ - array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, - }, - statement::{ - iteration::{ForLoopInitializer, IterableLoopInitializer}, - ForInLoop, ForLoop, ForOfLoop, - }, - Keyword, Position, Punctuator, - }, lexer::{Error as LexError, TokenKind}, parser::{ expression::Expression, @@ -27,6 +16,14 @@ use crate::syntax::{ AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, + statement::{ + iteration::{ForLoopInitializer, IterableLoopInitializer}, + ForInLoop, ForLoop, ForOfLoop, + }, + Keyword, Position, Punctuator, +}; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use rustc_hash::FxHashSet; @@ -357,26 +354,20 @@ fn initializer_to_iterable_loop_initializer( ))) } ast::Expression::Identifier(ident) => Ok(IterableLoopInitializer::Identifier(ident)), - ast::Expression::ArrayLiteral(array) => { - array_decl_to_declaration_pattern(&array, strict) - .ok_or(ParseError::General { - message: " - invalid array destructuring pattern in iterable loop initializer - ", - position, - }) - .map(|arr| IterableLoopInitializer::Pattern(arr.into())) - } - ast::Expression::ObjectLiteral(object) => { - object_decl_to_declaration_pattern(&object, strict) - .ok_or(ParseError::General { - message: " - invalid object destructuring pattern in iterable loop initializer - ", - position, - }) - .map(|obj| IterableLoopInitializer::Pattern(obj.into())) - } + ast::Expression::ArrayLiteral(array) => array + .to_pattern(strict) + .ok_or(ParseError::General { + message: "invalid array destructuring pattern in iterable loop initializer", + position, + }) + .map(|arr| IterableLoopInitializer::Pattern(arr.into())), + ast::Expression::ObjectLiteral(object) => object + .to_pattern(strict) + .ok_or(ParseError::General { + message: "invalid object destructuring pattern in iterable loop initializer", + position, + }) + .map(|obj| IterableLoopInitializer::Pattern(obj.into())), ast::Expression::PropertyAccess(access) => Ok(IterableLoopInitializer::Access(access)), _ => Err(ParseError::lex(LexError::Syntax( "invalid variable for iterable loop".into(), diff --git a/boa_engine/src/syntax/parser/statement/iteration/tests.rs b/boa_engine/src/syntax/parser/statement/iteration/tests.rs index 8ecc62df31..2db171ccd1 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/tests.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/tests.rs @@ -1,18 +1,14 @@ -use crate::syntax::{ - ast::{ - declaration::{VarDeclaration, Variable}, - expression::{ - access::SimplePropertyAccess, - literal::Literal, - operator::{ - assign::AssignOp, binary::RelationalOp, unary::UnaryOp, Assign, Binary, Unary, - }, - Call, Identifier, - }, - statement::{Block, Break, DoWhileLoop, WhileLoop}, - Expression, Statement, StatementListItem, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{VarDeclaration, Variable}, + expression::{ + access::SimplePropertyAccess, + literal::Literal, + operator::{assign::AssignOp, binary::RelationalOp, unary::UnaryOp, Assign, Binary, Unary}, + Call, Identifier, }, - parser::tests::{check_invalid, check_parser}, + statement::{Block, Break, DoWhileLoop, WhileLoop}, + Expression, Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; 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 eff204947b..47d113b027 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs @@ -1,10 +1,8 @@ -use crate::syntax::{ - ast::{statement::WhileLoop, Keyword, Punctuator}, - parser::{ - expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, - ParseError, ParseResult, TokenParser, - }, +use crate::syntax::parser::{ + expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, + ParseError, ParseResult, TokenParser, }; +use boa_ast::{statement::WhileLoop, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; 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 b6993b1c26..b266d74275 100644 --- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs @@ -1,5 +1,4 @@ use crate::syntax::{ - ast::{self, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::Cursor, @@ -9,6 +8,7 @@ use crate::syntax::{ AllowYield, ParseResult, TokenParser, }, }; +use boa_ast::{self as ast, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/mod.rs b/boa_engine/src/syntax/parser/statement/mod.rs index 846116b625..5822e4de16 100644 --- a/boa_engine/src/syntax/parser/statement/mod.rs +++ b/boa_engine/src/syntax/parser/statement/mod.rs @@ -41,14 +41,14 @@ use super::{ TokenParser, }; use crate::syntax::{ - ast::{ - self, - pattern::{ArrayPattern, ArrayPatternElement, ObjectPatternElement}, - Keyword, Punctuator, - }, lexer::{Error as LexError, InputElement, Token, TokenKind}, parser::expression::{BindingIdentifier, Initializer}, }; +use boa_ast::{ + self as ast, + pattern::{ArrayPattern, ArrayPatternElement, ObjectPatternElement}, + Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; 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 ea94ff190f..b986db7a63 100644 --- a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/return_stm/mod.rs @@ -1,5 +1,4 @@ use crate::syntax::{ - ast::{statement::Return, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, @@ -7,6 +6,7 @@ use crate::syntax::{ AllowAwait, AllowYield, ParseResult, TokenParser, }, }; +use boa_ast::{statement::Return, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/switch/mod.rs b/boa_engine/src/syntax/parser/statement/switch/mod.rs index 9094e104c8..05f9950d41 100644 --- a/boa_engine/src/syntax/parser/statement/switch/mod.rs +++ b/boa_engine/src/syntax/parser/statement/switch/mod.rs @@ -2,13 +2,15 @@ mod tests; use crate::syntax::{ - ast::{self, expression::Identifier, statement, statement::Switch, Keyword, Punctuator}, lexer::TokenKind, parser::{ expression::Expression, statement::StatementList, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + self as ast, expression::Identifier, statement, statement::Switch, Keyword, Punctuator, +}; use boa_interner::Interner; use boa_profiler::Profiler; use rustc_hash::{FxHashMap, FxHashSet}; diff --git a/boa_engine/src/syntax/parser/statement/switch/tests.rs b/boa_engine/src/syntax/parser/statement/switch/tests.rs index 7306769b6d..06eb58a0d6 100644 --- a/boa_engine/src/syntax/parser/statement/switch/tests.rs +++ b/boa_engine/src/syntax/parser/statement/switch/tests.rs @@ -1,11 +1,9 @@ -use crate::syntax::{ - ast::{ - declaration::{LexicalDeclaration, Variable}, - expression::{access::SimplePropertyAccess, literal::Literal, Call, Identifier}, - statement::{Break, Case, Switch}, - Declaration, Expression, Statement, - }, - parser::tests::{check_invalid, check_parser}, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{LexicalDeclaration, Variable}, + expression::{access::SimplePropertyAccess, literal::Literal, Call, Identifier}, + statement::{Break, Case, Switch}, + Declaration, Expression, Statement, }; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/throw/mod.rs b/boa_engine/src/syntax/parser/statement/throw/mod.rs index c53e8bfcfd..78f17fbdba 100644 --- a/boa_engine/src/syntax/parser/statement/throw/mod.rs +++ b/boa_engine/src/syntax/parser/statement/throw/mod.rs @@ -2,10 +2,10 @@ mod tests; use crate::syntax::{ - ast::{statement::Throw, Keyword, Punctuator}, lexer::TokenKind, parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, }; +use boa_ast::{statement::Throw, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/throw/tests.rs b/boa_engine/src/syntax/parser/statement/throw/tests.rs index 8c869cb5b4..960a94a191 100644 --- a/boa_engine/src/syntax/parser/statement/throw/tests.rs +++ b/boa_engine/src/syntax/parser/statement/throw/tests.rs @@ -1,7 +1,5 @@ -use crate::syntax::{ - ast::{expression::literal::Literal, statement::Throw, Statement}, - parser::tests::check_parser, -}; +use crate::syntax::parser::tests::check_parser; +use boa_ast::{expression::literal::Literal, statement::Throw, Statement}; use boa_interner::Interner; use boa_macros::utf16; diff --git a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs index 3721f7bbcd..0cbc2906dc 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs @@ -1,11 +1,11 @@ use crate::syntax::{ - ast::{declaration::Binding, statement, Keyword, Punctuator}, lexer::TokenKind, parser::{ statement::{block::Block, ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern}, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{declaration::Binding, statement, Keyword, Punctuator}; use boa_interner::Interner; use boa_profiler::Profiler; use rustc_hash::FxHashSet; diff --git a/boa_engine/src/syntax/parser/statement/try_stm/finally.rs b/boa_engine/src/syntax/parser/statement/try_stm/finally.rs index 3a6262e0c7..9e670a7ba4 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/finally.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/finally.rs @@ -1,10 +1,7 @@ -use crate::syntax::{ - ast::{statement, Keyword}, - parser::{ - statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, - TokenParser, - }, +use crate::syntax::parser::{ + statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, TokenParser, }; +use boa_ast::{statement, Keyword}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/syntax/parser/statement/try_stm/mod.rs b/boa_engine/src/syntax/parser/statement/try_stm/mod.rs index 73aa05a93d..989a738e39 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/mod.rs @@ -7,10 +7,13 @@ mod tests; use self::{catch::Catch, finally::Finally}; use super::block::Block; use crate::syntax::{ - ast::{statement::Try, Keyword}, lexer::TokenKind, parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; +use boa_ast::{ + statement::{ErrorHandler, Try}, + Keyword, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -89,7 +92,7 @@ where }; let next_token = cursor.peek(0, interner)?; - let finally_block = if let Some(token) = next_token { + let finally = if let Some(token) = next_token { match token.kind() { TokenKind::Keyword((Keyword::Finally, true)) => { return Err(ParseError::general( @@ -107,6 +110,13 @@ where None }; - Ok(Try::new(try_clause, catch, finally_block)) + let handler = match (catch, finally) { + (Some(catch), None) => ErrorHandler::Catch(catch), + (None, Some(finally)) => ErrorHandler::Finally(finally), + (Some(catch), Some(finally)) => ErrorHandler::Full(catch, finally), + (None, None) => unreachable!(), + }; + + Ok(Try::new(try_clause, handler)) } } diff --git a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs index c789ffb126..ccebb3fef2 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs @@ -1,15 +1,13 @@ use std::convert::TryInto; -use crate::syntax::{ - ast::{ - declaration::{VarDeclaration, Variable}, - expression::{literal::Literal, Identifier}, - pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, - property::PropertyName, - statement::{Block, Catch, Finally, Try}, - Statement, StatementListItem, - }, - parser::tests::{check_invalid, check_parser}, +use crate::syntax::parser::tests::{check_invalid, check_parser}; +use boa_ast::{ + declaration::{VarDeclaration, Variable}, + expression::{literal::Literal, Identifier}, + pattern::{ArrayPatternElement, ObjectPatternElement, Pattern}, + property::PropertyName, + statement::{Block, Catch, ErrorHandler, Finally, Try}, + Statement, StatementListItem, }; use boa_interner::Interner; use boa_macros::utf16; @@ -21,11 +19,10 @@ fn check_inline_with_empty_try_catch() { "try { } catch(e) {}", vec![Statement::Try(Try::new( Block::default(), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), Block::default(), )), - None, )) .into()], interner, @@ -48,11 +45,10 @@ fn check_inline_with_var_decl_inside_try() { )) .into()] .into(), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), Block::default(), )), - None, )) .into()], interner, @@ -75,7 +71,7 @@ fn check_inline_with_var_decl_inside_catch() { )) .into()] .into(), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), vec![Statement::Var(VarDeclaration( vec![Variable::from_identifier( @@ -88,7 +84,6 @@ fn check_inline_with_var_decl_inside_catch() { .into()] .into(), )), - None, )) .into()], interner, @@ -102,11 +97,13 @@ fn check_inline_with_empty_try_catch_finally() { "try {} catch(e) {} finally {}", vec![Statement::Try(Try::new( Block::default(), - Some(Catch::new( - Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), - Block::default(), - )), - Some(Finally::from(Block::default())), + ErrorHandler::Full( + Catch::new( + Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), + Block::default(), + ), + Finally::from(Block::default()), + ), )) .into()], interner, @@ -119,8 +116,7 @@ fn check_inline_with_empty_try_finally() { "try {} finally {}", vec![Statement::Try(Try::new( Block::default(), - None, - Some(Finally::from(Block::default())), + ErrorHandler::Finally(Finally::from(Block::default())), )) .into()], Interner::default(), @@ -134,8 +130,7 @@ fn check_inline_with_empty_try_var_decl_in_finally() { "try {} finally { var x = 1; }", vec![Statement::Try(Try::new( Block::default(), - None, - Some(Finally::from(Block::from(vec![ + ErrorHandler::Finally(Finally::from(Block::from(vec![ StatementListItem::Statement(Statement::Var(VarDeclaration( vec![Variable::from_identifier( interner.get_or_intern_static("x", utf16!("x")).into(), @@ -158,7 +153,7 @@ fn check_inline_empty_try_paramless_catch() { "try {} catch { var x = 1; }", vec![Statement::Try(Try::new( Block::default(), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( None, vec![Statement::Var(VarDeclaration( vec![Variable::from_identifier( @@ -171,7 +166,6 @@ fn check_inline_empty_try_paramless_catch() { .into()] .into(), )), - None, )) .into()], interner, @@ -186,7 +180,7 @@ fn check_inline_with_binding_pattern_object() { "try {} catch ({ a, b: c }) {}", vec![Statement::Try(Try::new( Block::default(), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some( Pattern::from(vec![ ObjectPatternElement::SingleName { @@ -206,7 +200,6 @@ fn check_inline_with_binding_pattern_object() { ), Block::default(), )), - None, )) .into()], interner, @@ -220,7 +213,7 @@ fn check_inline_with_binding_pattern_array() { "try {} catch ([a, b]) {}", vec![Statement::Try(Try::new( Block::from(vec![]), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some( Pattern::from(vec![ ArrayPatternElement::SingleName { @@ -236,7 +229,6 @@ fn check_inline_with_binding_pattern_array() { ), Block::default(), )), - None, )) .into()], interner, @@ -250,7 +242,7 @@ fn check_catch_with_var_redeclaration() { "try {} catch(e) { var e = 'oh' }", vec![Statement::Try(Try::new( Block::from(vec![]), - Some(Catch::new( + ErrorHandler::Catch(Catch::new( Some(Identifier::new(interner.get_or_intern_static("e", utf16!("e"))).into()), vec![Statement::Var(VarDeclaration( vec![Variable::from_identifier( @@ -265,7 +257,6 @@ fn check_catch_with_var_redeclaration() { .into()] .into(), )), - None, )) .into()], interner, diff --git a/boa_engine/src/syntax/parser/statement/variable/mod.rs b/boa_engine/src/syntax/parser/statement/variable/mod.rs index 728975e5e5..a243b106d8 100644 --- a/boa_engine/src/syntax/parser/statement/variable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/variable/mod.rs @@ -1,20 +1,18 @@ //! Variable statement parsing. use crate::syntax::{ - ast::{ - declaration::{VarDeclaration, Variable}, - Keyword, Punctuator, Statement, - }, lexer::TokenKind, parser::{ - cursor::Cursor, expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, - AllowYield, ParseError, TokenParser, - }, - parser::{ - statement::{ArrayBindingPattern, ObjectBindingPattern}, - ParseResult, + cursor::Cursor, + expression::Initializer, + statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern}, + AllowAwait, AllowIn, AllowYield, ParseError, ParseResult, TokenParser, }, }; +use boa_ast::{ + declaration::{VarDeclaration, Variable}, + Keyword, Punctuator, Statement, +}; use boa_interner::Interner; use boa_profiler::Profiler; use std::{convert::TryInto, io::Read}; diff --git a/boa_engine/src/syntax/parser/tests/format/declaration.rs b/boa_engine/src/syntax/parser/tests/format/declaration.rs new file mode 100644 index 0000000000..cf951f4dc4 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/declaration.rs @@ -0,0 +1,42 @@ +use crate::syntax::parser::tests::format::test_formatting; + +#[test] +fn binding_pattern() { + test_formatting( + r#" + var { } = { + o: "1", + }; + var { o_v1 } = { + o_v1: "1", + }; + var { o_v2 = "1" } = { + o_v2: "2", + }; + var { a : o_v3 = "1" } = { + a: "2", + }; + var { ... o_rest_v1 } = { + a: "2", + }; + var { o_v4, o_v5, o_v6 = "1", a : o_v7 = "1", ... o_rest_v2 } = { + o_v4: "1", + o_v5: "1", + }; + var [] = []; + var [ , ] = []; + var [ a_v1 ] = [1, 2, 3]; + var [ a_v2, a_v3 ] = [1, 2, 3]; + var [ a_v2, , a_v3 ] = [1, 2, 3]; + var [ ... a_rest_v1 ] = [1, 2, 3]; + var [ a_v4, , ... a_rest_v2 ] = [1, 2, 3]; + var [ { a_v5 } ] = [{ + a_v5: 1, + }, { + a_v5: 2, + }, { + a_v5: 3, + }]; + "#, + ); +} diff --git a/boa_engine/src/syntax/parser/tests/format/expression.rs b/boa_engine/src/syntax/parser/tests/format/expression.rs new file mode 100644 index 0000000000..da20dec2e9 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/expression.rs @@ -0,0 +1,137 @@ +use crate::syntax::parser::tests::format::test_formatting; + +#[test] +fn new() { + test_formatting( + r#" + function MyClass() {} + let inst = new MyClass(); + "#, + ); +} + +#[test] +fn call() { + test_formatting( + r#" + call_1(1, 2, 3); + call_2("argument here"); + call_3(); + "#, + ); +} + +#[test] +fn assign() { + test_formatting( + r#" + let a = 20; + a += 10; + a -= 10; + a *= 10; + a **= 10; + a /= 10; + a %= 10; + a &= 10; + a |= 10; + a ^= 10; + a <<= 10; + a >>= 10; + a >>>= 10; + a &&= 10; + a ||= 10; + a ??= 10; + a; + "#, + ); +} + +#[test] +fn spread() { + test_formatting( + r#" + function f(m) { + return m; + } + function g(...args) { + return f(...args); + } + let a = g("message"); + a; + "#, + ); +} + +#[test] +fn r#await() { + // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented. + test_formatting( + r#" + async function f() { + await function_call(); + } + "#, + ); +} + +#[test] +fn array() { + test_formatting( + r#" + let a = [1, 2, 3, "words", "more words"]; + let b = []; + "#, + ); +} + +#[test] +fn template() { + test_formatting( + r#" + function tag(t, ...args) { + let a = []; + a = a.concat([t[0], t[1], t[2]]); + a = a.concat([t.raw[0], t.raw[1], t.raw[2]]); + a = a.concat([args[0], args[1]]); + return a; + } + let a = 10; + tag`result: ${a} \x26 ${a + 10}`; + "#, + ); +} + +#[test] +fn object() { + test_formatting( + r#" + let other = { + c: 10, + }; + let inst = { + val: 5, + b: "hello world", + nested: { + a: 5, + b: 6, + }, + ...other, + say_hi: function() { + console.log("hello!"); + }, + get a() { + return this.val + 1; + }, + set a(new_value) { + this.val = new_value; + }, + say_hello(msg) { + console.log("hello " + msg); + }, + }; + inst.a = 20; + inst.a; + inst.say_hello("humans"); + "#, + ); +} diff --git a/boa_engine/src/syntax/parser/tests/format/function/class.rs b/boa_engine/src/syntax/parser/tests/format/function/class.rs new file mode 100644 index 0000000000..4563105a31 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/function/class.rs @@ -0,0 +1,106 @@ +use crate::syntax::parser::tests::format::test_formatting; + +#[test] +fn class_declaration_empty() { + test_formatting( + r#" + class A {} + "#, + ); +} + +#[test] +fn class_declaration_empty_extends() { + test_formatting( + r#" + class A extends Object {} + "#, + ); +} + +#[test] +fn class_declaration_constructor() { + test_formatting( + r#" + class A { + constructor(a, b, c) { + this.value = a + b + c; + } + } + "#, + ); +} + +#[test] +fn class_declaration_elements() { + test_formatting( + r#" + class A { + a; + b = 1; + c() {} + d(a, b, c) { + return a + b + c; + } + set e(value) {} + get e() {} + set(a, b) {} + get(a, b) {} + } + "#, + ); +} + +#[test] +fn class_declaration_elements_private() { + test_formatting( + r#" + class A { + #a; + #b = 1; + #c() {} + #d(a, b, c) { + return a + b + c; + } + set #e(value) {} + get #e() {} + } + "#, + ); +} + +#[test] +fn class_declaration_elements_static() { + test_formatting( + r#" + class A { + static a; + static b = 1; + static c() {} + static d(a, b, c) { + return a + b + c; + } + static set e(value) {} + static get e() {} + } + "#, + ); +} + +#[test] +fn class_declaration_elements_private_static() { + test_formatting( + r#" + class A { + static #a; + static #b = 1; + static #c() {} + static #d(a, b, c) { + return a + b + c; + } + static set #e(value) {} + static get #e() {} + } + "#, + ); +} diff --git a/boa_engine/src/syntax/parser/tests/format/function/mod.rs b/boa_engine/src/syntax/parser/tests/format/function/mod.rs new file mode 100644 index 0000000000..4c8417a862 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/function/mod.rs @@ -0,0 +1,48 @@ +use crate::syntax::parser::tests::format::test_formatting; + +mod class; + +#[test] +fn function() { + test_formatting( + r#" + function func(a, b) { + console.log(a); + } + function func_2(a, b) {} + pass_func(function(a, b) { + console.log("in callback", a); + }); + pass_func(function(a, b) {}); + "#, + ); +} + +#[test] +fn arrow() { + test_formatting( + r#" + let arrow_func = (a, b) => { + console.log("in multi statement arrow"); + console.log(b); + }; + let arrow_func_2 = (a, b) => {}; + "#, + ); +} + +#[test] +fn r#async() { + test_formatting( + r#" + async function async_func(a, b) { + console.log(a); + } + async function async_func_2(a, b) {} + pass_async_func(async function(a, b) { + console.log("in async callback", a); + }); + pass_async_func(async function(a, b) {}); + "#, + ); +} diff --git a/boa_engine/src/syntax/parser/tests/format/mod.rs b/boa_engine/src/syntax/parser/tests/format/mod.rs new file mode 100644 index 0000000000..7df0dc9e15 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/mod.rs @@ -0,0 +1,47 @@ +mod declaration; +mod expression; +mod function; +mod statement; + +/// This parses the given source code, and then makes sure that +/// the resulting `StatementList` is formatted in the same manner +/// as the source code. This is expected to have a preceding +/// newline. +/// +/// This is a utility function for tests. It was made in case people +/// are using different indents in their source files. This fixes +/// any strings which may have been changed in a different indent +/// level. +#[cfg(test)] +fn test_formatting(source: &'static str) { + // Remove preceding newline. + + use boa_interner::ToInternedString; + + use crate::{syntax::Parser, Context}; + let source = &source[1..]; + + // Find out how much the code is indented + let first_line = &source[..source.find('\n').unwrap()]; + let trimmed_first_line = first_line.trim(); + let characters_to_remove = first_line.len() - trimmed_first_line.len(); + + let scenario = source + .lines() + .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line + .collect::>() + .join("\n"); + let mut context = Context::default(); + let result = Parser::new(scenario.as_bytes()) + .parse_all(&mut context) + .expect("parsing failed") + .to_interned_string(context.interner()); + if scenario != result { + eprint!("========= Expected:\n{scenario}"); + eprint!("========= Got:\n{result}"); + // Might be helpful to find differing whitespace + eprintln!("========= Expected: {scenario:?}"); + eprintln!("========= Got: {result:?}"); + panic!("parsing test did not give the correct result (see above)"); + } +} diff --git a/boa_engine/src/syntax/parser/tests/format/statement.rs b/boa_engine/src/syntax/parser/tests/format/statement.rs new file mode 100644 index 0000000000..ca6794db51 --- /dev/null +++ b/boa_engine/src/syntax/parser/tests/format/statement.rs @@ -0,0 +1,124 @@ +use crate::syntax::parser::tests::format::test_formatting; + +#[test] +fn block() { + test_formatting( + r#" + { + let a = function_call(); + console.log("hello"); + } + another_statement(); + "#, + ); + // TODO: Once block labels are implemtned, this should be tested: + // super::super::test_formatting( + // r#" + // block_name: { + // let a = function_call(); + // console.log("hello"); + // } + // another_statement(); + // "#, + // ); +} + +#[test] +fn r#if() { + test_formatting( + r#" + let a = true ? 5 : 6; + if (false) { + a = 10; + } else { + a = 20; + } + "#, + ); +} + +#[test] +fn r#return() { + test_formatting( + r#" + function say_hello(msg) { + if (msg === "") { + return 0; + } + console.log("hello " + msg); + return; + } + say_hello(""); + say_hello("world"); + "#, + ); +} + +#[test] +fn throw() { + test_formatting( + r#" + try { + throw "hello"; + } catch(e) { + console.log(e); + } + "#, + ); +} + +#[test] +fn r#try() { + test_formatting( + r#" + try { + throw "hello"; + } catch(e) { + console.log(e); + } finally { + console.log("things"); + } + try { + throw "hello"; + } catch { + console.log("something went wrong"); + } + "#, + ); +} + +#[test] +fn switch() { + test_formatting( + r#" + let a = 3; + let b = "unknown"; + switch (a) { + case 0: + b = "Mon"; + break; + case 1: + b = "Tue"; + break; + case 2: + b = "Wed"; + break; + case 3: + b = "Thurs"; + break; + case 4: + b = "Fri"; + break; + case 5: + b = "Sat"; + break; + case 6: + b = "Sun"; + break; + default: + b = "Unknown"; + } + b; + "#, + ); +} diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests/mod.rs similarity index 92% rename from boa_engine/src/syntax/parser/tests.rs rename to boa_engine/src/syntax/parser/tests/mod.rs index 4d6aa24b65..12b9c35518 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/boa_engine/src/syntax/parser/tests/mod.rs @@ -1,32 +1,30 @@ //! Tests for the parser. +mod format; + use std::convert::TryInto; use super::Parser; -use crate::{ - context::ContextBuilder, - string::utf16, - syntax::ast::{ - declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable}, - expression::{ - access::SimplePropertyAccess, - literal::{Literal, ObjectLiteral}, - operator::{ - assign::AssignOp, - binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp}, - unary::UnaryOp, - Assign, Binary, Unary, - }, - Call, Identifier, New, - }, - function::{ - ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function, +use crate::{context::ContextBuilder, string::utf16, Context}; +use boa_ast::{ + declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable}, + expression::{ + access::SimplePropertyAccess, + literal::{Literal, ObjectLiteral}, + operator::{ + assign::AssignOp, + binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp}, + unary::UnaryOp, + Assign, Binary, Unary, }, - property::PropertyDefinition, - statement::{If, Return}, - Expression, Statement, StatementList, StatementListItem, + Call, Identifier, New, + }, + function::{ + ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function, }, - Context, + property::PropertyDefinition, + statement::{If, Return}, + Expression, Statement, StatementList, StatementListItem, }; use boa_interner::Interner; @@ -485,19 +483,17 @@ fn spread_in_arrow_function() { let mut interner = Interner::default(); let b = interner.get_or_intern_static("b", utf16!("b")); + let params = FormalParameterList::from(FormalParameter::new( + Variable::from_identifier(b.into(), None), + true, + )); + assert_eq!(params.flags(), FormalParameterListFlags::HAS_REST_PARAMETER); + assert_eq!(params.length(), 0); check_parser( s, vec![Statement::Expression(Expression::from(ArrowFunction::new( None, - FormalParameterList { - parameters: Box::new([FormalParameter::new( - Variable::from_identifier(b.into(), None), - true, - )]), - flags: FormalParameterListFlags::empty() - .union(FormalParameterListFlags::HAS_REST_PARAMETER), - length: 0, - }, + params, vec![Statement::Expression(Expression::from(Identifier::from(b))).into()].into(), ))) .into()], diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs index a0bc3295bf..4a0cda6ada 100644 --- a/boa_engine/src/tests.rs +++ b/boa_engine/src/tests.rs @@ -1615,6 +1615,123 @@ fn test_strict_mode_dup_func_parameters() { )]); } +#[test] +fn strict_mode_global() { + let scenario = r#" + 'use strict'; + let throws = false; + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "true")]); +} + +#[test] +fn strict_mode_function() { + let scenario = r#" + let throws = false; + function t() { + 'use strict'; + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + } + t() + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "true")]); +} + +#[test] +fn strict_mode_function_after() { + let scenario = r#" + function t() { + 'use strict'; + } + t() + let throws = false; + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "false")]); +} + +#[test] +fn strict_mode_global_active_in_function() { + let scenario = r#" + 'use strict' + let throws = false; + function a(){ + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + } + a(); + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "true")]); +} + +#[test] +fn strict_mode_function_in_function() { + let scenario = r#" + let throws = false; + function a(){ + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + } + function b(){ + 'use strict'; + a(); + } + b(); + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "false")]); +} + +#[test] +fn strict_mode_function_return() { + let scenario = r#" + let throws = false; + function a() { + 'use strict'; + + return function () { + try { + delete Boolean.prototype; + } catch (e) { + throws = true; + } + } + } + a()(); + throws + "#; + + check_output(&[TestAction::TestEq(scenario, "true")]); +} + #[test] fn test_empty_statement() { let src = r#" @@ -1643,3 +1760,1077 @@ fn test_labelled_block() { "#; assert_eq!(&exec(src), "true"); } + +#[test] +fn simple_try() { + let scenario = r#" + let a = 10; + try { + a = 20; + } catch { + a = 30; + } + + a; + "#; + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn finally() { + let scenario = r#" + let a = 10; + try { + a = 20; + } finally { + a = 30; + } + + a; + "#; + assert_eq!(&exec(scenario), "30"); +} + +#[test] +fn catch_finally() { + let scenario = r#" + let a = 10; + try { + a = 20; + } catch { + a = 40; + } finally { + a = 30; + } + + a; + "#; + assert_eq!(&exec(scenario), "30"); +} + +#[test] +fn catch() { + let scenario = r#" + let a = 10; + try { + throw "error"; + } catch { + a = 20; + } + + a; + "#; + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn catch_binding() { + let scenario = r#" + let a = 10; + try { + throw 20; + } catch(err) { + a = err; + } + + a; + "#; + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn catch_binding_pattern_object() { + let scenario = r#" + let a = 10; + try { + throw { + n: 30, + }; + } catch ({ n }) { + a = n; + } + + a; + "#; + assert_eq!(&exec(scenario), "30"); +} + +#[test] +fn catch_binding_pattern_array() { + let scenario = r#" + let a = 10; + try { + throw [20, 30]; + } catch ([, n]) { + a = n; + } + + a; + "#; + assert_eq!(&exec(scenario), "30"); +} + +#[test] +fn catch_binding_finally() { + let scenario = r#" + let a = 10; + try { + throw 20; + } catch(err) { + a = err; + } finally { + a = 30; + } + + a; + "#; + assert_eq!(&exec(scenario), "30"); +} + +#[test] +fn single_case_switch() { + let scenario = r#" + let a = 10; + switch (a) { + case 10: + a = 20; + break; + } + + a; + "#; + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn no_cases_switch() { + let scenario = r#" + let a = 10; + switch (a) { + } + + a; + "#; + assert_eq!(&exec(scenario), "10"); +} + +#[test] +fn no_true_case_switch() { + let scenario = r#" + let a = 10; + switch (a) { + case 5: + a = 15; + break; + } + + a; + "#; + assert_eq!(&exec(scenario), "10"); +} + +#[test] +fn two_case_switch() { + let scenario = r#" + let a = 10; + switch (a) { + case 5: + a = 15; + break; + case 10: + a = 20; + break; + } + + a; + "#; + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn two_case_no_break_switch() { + let scenario = r#" + let a = 10; + let b = 10; + + switch (a) { + case 10: + a = 150; + case 20: + b = 150; + break; + } + + a + b; + "#; + assert_eq!(&exec(scenario), "300"); +} + +#[test] +fn three_case_partial_fallthrough() { + let scenario = r#" + let a = 10; + let b = 10; + + switch (a) { + case 10: + a = 150; + case 20: + b = 150; + break; + case 15: + b = 1000; + break; + } + + a + b; + "#; + assert_eq!(&exec(scenario), "300"); +} + +#[test] +fn default_taken_switch() { + let scenario = r#" + let a = 10; + + switch (a) { + case 5: + a = 150; + break; + default: + a = 70; + } + + a; + "#; + assert_eq!(&exec(scenario), "70"); +} + +#[test] +fn default_not_taken_switch() { + let scenario = r#" + let a = 5; + + switch (a) { + case 5: + a = 150; + break; + default: + a = 70; + } + + a; + "#; + assert_eq!(&exec(scenario), "150"); +} + +#[test] +fn string_switch() { + let scenario = r#" + let a = "hello"; + + switch (a) { + case "hello": + a = "world"; + break; + default: + a = "hi"; + } + + a; + "#; + assert_eq!(&exec(scenario), "\"world\""); +} + +#[test] +fn bigger_switch_example() { + let expected = [ + "\"Mon\"", + "\"Tue\"", + "\"Wed\"", + "\"Thurs\"", + "\"Fri\"", + "\"Sat\"", + "\"Sun\"", + ]; + + for (i, val) in expected.iter().enumerate() { + let scenario = format!( + r#" + let a = {i}; + let b = "unknown"; + + switch (a) {{ + case 0: + b = "Mon"; + break; + case 1: + b = "Tue"; + break; + case 2: + b = "Wed"; + break; + case 3: + b = "Thurs"; + break; + case 4: + b = "Fri"; + break; + case 5: + b = "Sat"; + break; + case 6: + b = "Sun"; + break; + }} + + b; + + "#, + ); + + assert_eq!(&exec(&scenario), val); + } +} + +#[test] +fn while_loop_late_break() { + // Ordering with statement before the break. + let scenario = r#" + let a = 1; + while (a < 5) { + a++; + if (a == 3) { + break; + } + } + a; + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn while_loop_early_break() { + // Ordering with statements after the break. + let scenario = r#" + let a = 1; + while (a < 5) { + if (a == 3) { + break; + } + a++; + } + a; + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn for_loop_break() { + let scenario = r#" + let a = 1; + for (; a < 5; a++) { + if (a == 3) { + break; + } + } + a; + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn for_loop_return() { + let scenario = r#" + function foo() { + for (let a = 1; a < 5; a++) { + if (a == 3) { + return a; + } + } + } + + foo(); + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn do_loop_late_break() { + // Ordering with statement before the break. + let scenario = r#" + let a = 1; + do { + a++; + if (a == 3) { + break; + } + } while (a < 5); + a; + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn do_loop_early_break() { + // Ordering with statements after the break. + let scenario = r#" + let a = 1; + do { + if (a == 3) { + break; + } + a++; + } while (a < 5); + a; + "#; + + assert_eq!(&exec(scenario), "3"); +} + +#[test] +fn break_out_of_inner_loop() { + let scenario = r#" + var a = 0, b = 0; + for (let i = 0; i < 2; i++) { + a++; + for (let j = 0; j < 10; j++) { + b++; + if (j == 3) + break; + } + a++; + } + [a, b] + "#; + assert_eq!(&exec(scenario), "[ 4, 8 ]"); +} + +#[test] +fn continue_inner_loop() { + let scenario = r#" + var a = 0, b = 0; + for (let i = 0; i < 2; i++) { + a++; + for (let j = 0; j < 10; j++) { + if (j < 3) + continue; + b++; + } + a++; + } + [a, b] + "#; + assert_eq!(&exec(scenario), "[ 4, 14 ]"); +} + +#[test] +fn for_loop_continue_out_of_switch() { + let scenario = r#" + var a = 0, b = 0, c = 0; + for (let i = 0; i < 3; i++) { + a++; + switch (i) { + case 0: + continue; + c++; + case 1: + continue; + case 5: + c++; + } + b++; + } + [a, b, c] + "#; + assert_eq!(&exec(scenario), "[ 3, 1, 0 ]"); +} + +#[test] +fn while_loop_continue() { + let scenario = r#" + var i = 0, a = 0, b = 0; + while (i < 3) { + i++; + if (i < 2) { + a++; + continue; + } + b++; + } + [a, b] + "#; + assert_eq!(&exec(scenario), "[ 1, 2 ]"); +} + +#[test] +fn do_while_loop_continue() { + let scenario = r#" + var i = 0, a = 0, b = 0; + do { + i++; + if (i < 2) { + a++; + continue; + } + b++; + } while (i < 3) + [a, b] + "#; + assert_eq!(&exec(scenario), "[ 1, 2 ]"); +} + +#[test] +fn for_of_loop_declaration() { + let scenario = r#" + var result = 0; + for (i of [1, 2, 3]) { + result = i; + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq("i", "3"), + ]); +} + +#[test] +fn for_of_loop_var() { + let scenario = r#" + var result = 0; + for (var i of [1, 2, 3]) { + result = i; + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq("i", "3"), + ]); +} + +#[test] +fn for_of_loop_let() { + let scenario = r#" + var result = 0; + for (let i of [1, 2, 3]) { + result = i; + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq( + r#" + try { + i + } catch(e) { + e.toString() + } + "#, + "\"ReferenceError: i is not defined\"", + ), + ]); +} + +#[test] +fn for_of_loop_const() { + let scenario = r#" + var result = 0; + for (let i of [1, 2, 3]) { + result = i; + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq( + r#" + try { + i + } catch(e) { + e.toString() + } + "#, + "\"ReferenceError: i is not defined\"", + ), + ]); +} + +#[test] +fn for_of_loop_break() { + let scenario = r#" + var result = 0; + for (var i of [1, 2, 3]) { + if (i > 1) + break; + result = i + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "1"), + TestAction::TestEq("i", "2"), + ]); +} + +#[test] +fn for_of_loop_continue() { + let scenario = r#" + var result = 0; + for (var i of [1, 2, 3]) { + if (i == 3) + continue; + result = i + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "2"), + TestAction::TestEq("i", "3"), + ]); +} + +#[test] +fn for_of_loop_return() { + let scenario = r#" + function foo() { + for (i of [1, 2, 3]) { + if (i > 1) + return i; + } + } + "#; + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("foo()", "2"), + ]); +} + +#[test] +fn for_loop_break_label() { + let scenario = r#" + var str = ""; + + outer: for (let i = 0; i < 5; i++) { + inner: for (let b = 0; b < 5; b++) { + if (b === 2) { + break outer; + } + str = str + b; + } + str = str + i; + } + str + "#; + assert_eq!(&exec(scenario), "\"01\""); +} + +#[test] +fn for_loop_continue_label() { + let scenario = r#" + var count = 0; + label: for (let x = 0; x < 10;) { + while (true) { + x++; + count++; + continue label; + } + } + count + "#; + assert_eq!(&exec(scenario), "10"); +} + +#[test] +fn for_in_declaration() { + let init = r#" + let result = []; + let obj = { a: "a", b: 2}; + var i; + for (i in obj) { + result = result.concat([i]); + } + "#; + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", + ), + ]); +} + +#[test] +fn for_in_var_object() { + let init = r#" + let result = []; + let obj = { a: "a", b: 2}; + for (var i in obj) { + result = result.concat([i]); + } + "#; + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", + ), + ]); +} + +#[test] +fn for_in_var_array() { + let init = r#" + let result = []; + let arr = ["a", "b"]; + for (var i in arr) { + result = result.concat([i]); + } + "#; + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('0') && result.includes('1')", + "true", + ), + ]); +} + +#[test] +fn for_in_let_object() { + let init = r#" + let result = []; + let obj = { a: "a", b: 2}; + for (let i in obj) { + result = result.concat([i]); + } + "#; + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", + ), + ]); +} + +#[test] +fn for_in_const_array() { + let init = r#" + let result = []; + let arr = ["a", "b"]; + for (const i in arr) { + result = result.concat([i]); + } + "#; + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('0') && result.includes('1')", + "true", + ), + ]); +} + +#[test] +fn for_in_break_label() { + let scenario = r#" + var str = ""; + + outer: for (let i in [1, 2]) { + inner: for (let b in [2, 3, 4]) { + if (b === "1") { + break outer; + } + str = str + b; + } + str = str + i; + } + str + "#; + assert_eq!(&exec(scenario), "\"0\""); +} + +#[test] +fn for_in_continue_label() { + let scenario = r#" + var str = ""; + + outer: for (let i in [1, 2]) { + inner: for (let b in [2, 3, 4]) { + if (b === "1") { + continue outer; + } + str = str + b; + } + str = str + i; + } + str + "#; + assert_eq!(&exec(scenario), "\"00\""); +} + +#[test] +fn tagged_template() { + let scenario = r#" + function tag(t, ...args) { + let a = [] + a = a.concat([t[0], t[1], t[2]]); + a = a.concat([t.raw[0], t.raw[1], t.raw[2]]); + a = a.concat([args[0], args[1]]); + return a + } + let a = 10; + tag`result: ${a} \x26 ${a+10}`; + "#; + + assert_eq!( + &exec(scenario), + r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"# + ); +} + +#[test] +fn spread_shallow_clone() { + let scenario = r#" + var a = { x: {} }; + var aClone = { ...a }; + + a.x === aClone.x + "#; + assert_eq!(&exec(scenario), "true"); +} + +#[test] +fn spread_merge() { + let scenario = r#" + var a = { x: 1, y: 2 }; + var b = { x: -1, z: -3, ...a }; + + (b.x === 1) && (b.y === 2) && (b.z === -3) + "#; + assert_eq!(&exec(scenario), "true"); +} + +#[test] +fn spread_overriding_properties() { + let scenario = r#" + var a = { x: 0, y: 0 }; + var aWithOverrides = { ...a, ...{ x: 1, y: 2 } }; + + (aWithOverrides.x === 1) && (aWithOverrides.y === 2) + "#; + assert_eq!(&exec(scenario), "true"); +} + +#[test] +fn spread_getters_in_initializer() { + let scenario = r#" + var a = { x: 42 }; + var aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } }; + "#; + assert_eq!(&exec(scenario), "undefined"); +} + +#[test] +fn spread_getters_in_object() { + let scenario = r#" + var a = { x: 42 }; + var aWithXGetter = { ...a, ... { get x() { throw new Error('not thrown yet') } } }; + "#; + assert_eq!(&exec(scenario), "\"Error\": \"not thrown yet\""); +} + +#[test] +fn spread_setters() { + let scenario = r#" + var z = { set x(nexX) { throw new Error() }, ... { x: 1 } }; + "#; + assert_eq!(&exec(scenario), "undefined"); +} + +#[test] +fn spread_null_and_undefined_ignored() { + let scenario = r#" + var a = { ...null, ...undefined }; + var count = 0; + + for (key in a) { count++; } + + count === 0 + "#; + + assert_eq!(&exec(scenario), "true"); +} + +#[test] +fn template_literal() { + let scenario = r#" + let a = 10; + `result: ${a} and ${a+10}`; + "#; + + assert_eq!(&exec(scenario), "\"result: 10 and 20\""); +} + +#[test] +fn assignmentoperator_lhs_not_defined() { + let scenario = r#" + try { + a += 5 + } catch (err) { + err.toString() + } + "#; + + assert_eq!(&exec(scenario), "\"ReferenceError: a is not defined\""); +} + +#[test] +fn assignmentoperator_rhs_throws_error() { + let scenario = r#" + try { + let a; + a += b + } catch (err) { + err.toString() + } + "#; + + assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\""); +} + +#[test] +fn instanceofoperator_rhs_not_object() { + let scenario = r#" + try { + let s = new String(); + s instanceof 1 + } catch (err) { + err.toString() + } + "#; + + assert_eq!( + &exec(scenario), + "\"TypeError: right-hand side of 'instanceof' should be an object, got number\"" + ); +} + +#[test] +fn instanceofoperator_rhs_not_callable() { + let scenario = r#" + try { + let s = new String(); + s instanceof {} + } catch (err) { + err.toString() + } + "#; + + assert_eq!( + &exec(scenario), + "\"TypeError: right-hand side of 'instanceof' is not callable\"" + ); +} + +#[test] +fn logical_nullish_assignment() { + let scenario = r#" + let a = undefined; + a ??= 10; + a; + "#; + + assert_eq!(&exec(scenario), "10"); + + let scenario = r#" + let a = 20; + a ??= 10; + a; + "#; + + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn logical_assignment() { + let scenario = r#" + let a = false; + a &&= 10; + a; + "#; + + assert_eq!(&exec(scenario), "false"); + + let scenario = r#" + let a = 20; + a &&= 10; + a; + "#; + + assert_eq!(&exec(scenario), "10"); + + let scenario = r#" + let a = null; + a ||= 10; + a; + "#; + + assert_eq!(&exec(scenario), "10"); + let scenario = r#" + let a = 20; + a ||= 10; + a; + "#; + + assert_eq!(&exec(scenario), "20"); +} + +#[test] +fn duplicate_function_name() { + let scenario = r#" + function f () {} + function f () {return 12;} + f() + "#; + + assert_eq!(&exec(scenario), "12"); +} + +#[test] +fn spread_with_new() { + let scenario = r#" + function F(m) { + this.m = m; + } + function f(...args) { + return new F(...args); + } + let a = f('message'); + a.m; + "#; + assert_eq!(&exec(scenario), r#""message""#); +} + +#[test] +fn spread_with_call() { + let scenario = r#" + function f(m) { + return m; + } + function g(...args) { + return f(...args); + } + let a = g('message'); + a; + "#; + assert_eq!(&exec(scenario), r#""message""#); +} diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index e411decf85..e83e085bbf 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -19,11 +19,11 @@ use crate::{ internal_methods::get_prototype_from_constructor, JsObject, ObjectData, PrivateElement, }, property::PropertyDescriptor, - syntax::ast::{expression::Identifier, function::FormalParameterList}, vm::call_frame::GeneratorResumeKind, vm::{call_frame::FinallyReturn, CallFrame, Opcode}, Context, JsResult, JsString, JsValue, }; +use boa_ast::{expression::Identifier, function::FormalParameterList}; use boa_gc::{Cell, Finalize, Gc, Trace}; use boa_interner::{Interner, Sym, ToInternedString}; use boa_profiler::Profiler; @@ -775,11 +775,11 @@ impl JsObject { let arg_count = args.len(); // Push function arguments to the stack. - let args = if code.params.parameters.len() > args.len() { + let args = if code.params.as_ref().len() > args.len() { let mut v = args.to_vec(); v.extend(vec![ JsValue::Undefined; - code.params.parameters.len() - args.len() + code.params.as_ref().len() - args.len() ]); v } else { @@ -790,7 +790,7 @@ impl JsObject { context.vm.push(arg); } - let param_count = code.params.parameters.len(); + let param_count = code.params.as_ref().len(); let has_expressions = code.params.has_expressions(); context.vm.push_frame(CallFrame { @@ -898,11 +898,11 @@ impl JsObject { let arg_count = args.len(); // Push function arguments to the stack. - let args = if code.params.parameters.len() > args.len() { + let args = if code.params.as_ref().len() > args.len() { let mut v = args.to_vec(); v.extend(vec![ JsValue::Undefined; - code.params.parameters.len() - args.len() + code.params.as_ref().len() - args.len() ]); v } else { @@ -913,7 +913,7 @@ impl JsObject { context.vm.push(arg); } - let param_count = code.params.parameters.len(); + let param_count = code.params.as_ref().len(); let has_expressions = code.params.has_expressions(); context.vm.push_frame(CallFrame { @@ -1015,11 +1015,11 @@ impl JsObject { let arg_count = args.len(); // Push function arguments to the stack. - let mut args = if code.params.parameters.len() > args.len() { + let mut args = if code.params.as_ref().len() > args.len() { let mut v = args.to_vec(); v.extend(vec![ JsValue::Undefined; - code.params.parameters.len() - args.len() + code.params.as_ref().len() - args.len() ]); v } else { @@ -1027,7 +1027,7 @@ impl JsObject { }; args.reverse(); - let param_count = code.params.parameters.len(); + let param_count = code.params.as_ref().len(); let call_frame = CallFrame { code, @@ -1150,11 +1150,11 @@ impl JsObject { let arg_count = args.len(); // Push function arguments to the stack. - let mut args = if code.params.parameters.len() > args.len() { + let mut args = if code.params.as_ref().len() > args.len() { let mut v = args.to_vec(); v.extend(vec![ JsValue::Undefined; - code.params.parameters.len() - args.len() + code.params.as_ref().len() - args.len() ]); v } else { @@ -1162,7 +1162,7 @@ impl JsObject { }; args.reverse(); - let param_count = code.params.parameters.len(); + let param_count = code.params.as_ref().len(); let call_frame = CallFrame { code, @@ -1368,7 +1368,7 @@ impl JsObject { let mut is_simple_parameter_list = true; let mut has_parameter_expressions = false; - for param in code.params.parameters.iter() { + for param in code.params.as_ref().iter() { has_parameter_expressions = has_parameter_expressions || param.init().is_some(); arguments_in_parameter_names = arguments_in_parameter_names || param.names().contains(&Sym::ARGUMENTS.into()); @@ -1401,11 +1401,11 @@ impl JsObject { let arg_count = args.len(); // Push function arguments to the stack. - let args = if code.params.parameters.len() > args.len() { + let args = if code.params.as_ref().len() > args.len() { let mut v = args.to_vec(); v.extend(vec![ JsValue::Undefined; - code.params.parameters.len() - args.len() + code.params.as_ref().len() - args.len() ]); v } else { @@ -1416,7 +1416,7 @@ impl JsObject { context.vm.push(arg); } - let param_count = code.params.parameters.len(); + let param_count = code.params.as_ref().len(); context.vm.push_frame(CallFrame { code, diff --git a/boa_examples/Cargo.toml b/boa_examples/Cargo.toml index daf754f80b..931c89374e 100644 --- a/boa_examples/Cargo.toml +++ b/boa_examples/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["console"] } +boa_ast.workspace = true boa_interner.workspace = true boa_gc.workspace = true gc = "0.4.1" diff --git a/boa_examples/src/bin/commuter_visitor.rs b/boa_examples/src/bin/commuter_visitor.rs index eee08b50e4..c53a1be0fa 100644 --- a/boa_examples/src/bin/commuter_visitor.rs +++ b/boa_examples/src/bin/commuter_visitor.rs @@ -2,17 +2,18 @@ // are used to swap the operands of commutable arithmetic operations. For an example which simply // inspects the AST without modifying it, see symbol_visitor.rs. -use boa_engine::syntax::ast::expression::operator::binary::{ArithmeticOp, BinaryOp}; -use boa_engine::syntax::ast::expression::operator::Binary; -use boa_engine::syntax::ast::visitor::{VisitWith, VisitorMut}; -use boa_engine::syntax::ast::Expression; -use boa_engine::syntax::Parser; -use boa_engine::Context; +use boa_ast::{ + expression::operator::{ + binary::{ArithmeticOp, BinaryOp}, + Binary, + }, + visitor::{VisitWith, VisitorMut}, + Expression, +}; +use boa_engine::{syntax::Parser, Context}; use boa_interner::ToInternedString; use core::ops::ControlFlow; -use std::convert::Infallible; -use std::fs::File; -use std::io::BufReader; +use std::{convert::Infallible, fs::File, io::BufReader}; /// Visitor which, when applied to a binary expression, will swap the operands. Use in other /// circumstances is undefined. @@ -44,21 +45,18 @@ impl<'ast> VisitorMut<'ast> for CommutorVisitor { type BreakTy = Infallible; fn visit_binary_mut(&mut self, node: &'ast mut Binary) -> ControlFlow { - match node.op() { - BinaryOp::Arithmetic(op) => { - match op { - ArithmeticOp::Add | ArithmeticOp::Mul => { - // set up the exchanger and swap lhs and rhs - let mut exchanger = OpExchanger::default(); - assert!(matches!( - exchanger.visit_binary_mut(node), - ControlFlow::Break(_) - )); - } - _ => {} + if let BinaryOp::Arithmetic(op) = node.op() { + match op { + ArithmeticOp::Add | ArithmeticOp::Mul => { + // set up the exchanger and swap lhs and rhs + let mut exchanger = OpExchanger::default(); + assert!(matches!( + exchanger.visit_binary_mut(node), + ControlFlow::Break(_) + )); } + _ => {} } - _ => {} } // traverse further in; there may nested binary operations node.visit_with_mut(self) diff --git a/boa_examples/src/bin/symbol_visitor.rs b/boa_examples/src/bin/symbol_visitor.rs index d1cd169846..8c5c898ff2 100644 --- a/boa_examples/src/bin/symbol_visitor.rs +++ b/boa_examples/src/bin/symbol_visitor.rs @@ -2,15 +2,11 @@ // AST, namely: finding all the `Sym`s present in a script. See commuter_visitor.rs for an example // which mutates the AST. -use boa_engine::syntax::ast::visitor::Visitor; -use boa_engine::syntax::Parser; -use boa_engine::Context; +use boa_ast::visitor::Visitor; +use boa_engine::{syntax::Parser, Context}; use boa_interner::Sym; use core::ops::ControlFlow; -use std::collections::HashSet; -use std::convert::Infallible; -use std::fs::File; -use std::io::BufReader; +use std::{collections::HashSet, convert::Infallible, fs::File, io::BufReader}; #[derive(Debug, Clone, Default)] struct SymbolVisitor {