From 25be262e574cd2ad5a5138a9e9ac7cc6076d7e86 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sat, 24 Sep 2022 03:01:54 -0500 Subject: [PATCH 1/8] Split `Node` into `Expression` and `Statement` --- boa_cli/src/main.rs | 2 +- boa_engine/src/builtins/eval/mod.rs | 2 +- boa_engine/src/builtins/function/arguments.rs | 2 +- boa_engine/src/builtins/function/mod.rs | 22 +- boa_engine/src/bytecompiler/function.rs | 50 +- boa_engine/src/bytecompiler/mod.rs | 1491 ++++++++--------- boa_engine/src/context/mod.rs | 8 +- .../src/syntax/ast/expression/access.rs | 187 +++ .../await_expr/mod.rs => expression/await.rs} | 47 +- boa_engine/src/syntax/ast/expression/call.rs | 116 ++ .../mod.rs => expression/identifier.rs} | 17 +- .../mod.rs => expression/literal/array.rs} | 66 +- .../literal/mod.rs} | 38 +- .../ast/expression/literal/object/mod.rs | 142 ++ .../literal}/object/tests.rs | 2 +- .../syntax/ast/expression/literal/template.rs | 98 ++ boa_engine/src/syntax/ast/expression/mod.rs | 583 +++++++ .../{node/new/mod.rs => expression/new.rs} | 29 +- .../ast/expression/operator/assign/mod.rs | 414 +++++ .../ast/expression/operator/assign/op.rs | 242 +++ .../ast/expression/operator/binary/mod.rs | 62 + .../ast/expression/operator/binary/op.rs | 564 +++++++ .../ast/expression/operator/conditional.rs | 64 + .../src/syntax/ast/expression/operator/mod.rs | 11 + .../{node => expression}/operator/tests.rs | 2 +- .../ast/expression/operator/unary/mod.rs | 60 + .../ast/expression/operator/unary/op.rs | 214 +++ .../src/syntax/ast/expression/spread.rs | 100 ++ .../syntax/ast/expression/tagged_template.rs | 91 + .../yield/mod.rs => expression/yield.rs} | 21 +- .../mod.rs => function/arrow_function.rs} | 72 +- .../mod.rs => function/async_function.rs} | 71 +- .../mod.rs => function/async_generator.rs} | 53 +- boa_engine/src/syntax/ast/function/class.rs | 527 ++++++ .../mod.rs => function/generator.rs} | 55 +- boa_engine/src/syntax/ast/function/mod.rs | 181 ++ .../ast/{node => function}/parameters.rs | 29 +- boa_engine/src/syntax/ast/keyword.rs | 19 +- boa_engine/src/syntax/ast/mod.rs | 100 +- boa_engine/src/syntax/ast/node/array/tests.rs | 9 - .../src/syntax/ast/node/await_expr/tests.rs | 9 - boa_engine/src/syntax/ast/node/block/tests.rs | 22 - boa_engine/src/syntax/ast/node/call/mod.rs | 69 - boa_engine/src/syntax/ast/node/call/tests.rs | 10 - .../node/conditional/conditional_op/mod.rs | 72 - .../src/syntax/ast/node/conditional/mod.rs | 9 - .../src/syntax/ast/node/conditional/tests.rs | 13 - .../declaration/async_function_decl/mod.rs | 88 - .../declaration/async_generator_decl/mod.rs | 86 - .../ast/node/declaration/class_decl/mod.rs | 366 ---- .../ast/node/declaration/class_decl/tests.rs | 106 -- .../ast/node/declaration/function_decl/mod.rs | 97 -- .../ast/node/declaration/function_expr/mod.rs | 93 - .../node/declaration/generator_decl/mod.rs | 88 - .../src/syntax/ast/node/declaration/mod.rs | 1114 ------------ .../src/syntax/ast/node/declaration/tests.rs | 82 - .../ast/node/field/get_const_field/mod.rs | 72 - .../syntax/ast/node/field/get_field/mod.rs | 72 - .../ast/node/field/get_private_field/mod.rs | 62 - .../ast/node/field/get_super_field/mod.rs | 47 - boa_engine/src/syntax/ast/node/field/mod.rs | 14 - boa_engine/src/syntax/ast/node/field/tests.rs | 10 - .../ast/node/iteration/break_node/tests.rs | 32 - .../syntax/ast/node/iteration/for_loop/mod.rs | 159 -- .../src/syntax/ast/node/iteration/mod.rs | 77 - boa_engine/src/syntax/ast/node/mod.rs | 1376 --------------- boa_engine/src/syntax/ast/node/new/tests.rs | 9 - .../syntax/ast/node/operator/assign/mod.rs | 440 ----- .../syntax/ast/node/operator/bin_op/mod.rs | 67 - .../src/syntax/ast/node/operator/mod.rs | 10 - .../syntax/ast/node/operator/unary_op/mod.rs | 55 - .../src/syntax/ast/node/return_smt/tests.rs | 16 - boa_engine/src/syntax/ast/node/spread/mod.rs | 59 - .../src/syntax/ast/node/spread/tests.rs | 47 - .../src/syntax/ast/node/super_call/mod.rs | 46 - .../src/syntax/ast/node/template/mod.rs | 135 -- .../src/syntax/ast/node/template/tests.rs | 48 - boa_engine/src/syntax/ast/node/throw/tests.rs | 12 - boa_engine/src/syntax/ast/op.rs | 1025 ----------- boa_engine/src/syntax/ast/pattern.rs | 811 +++++++++ .../ast/{node/object/mod.rs => property.rs} | 229 +-- boa_engine/src/syntax/ast/punctuator.rs | 99 +- .../{node/block/mod.rs => statement/block.rs} | 44 +- .../src/syntax/ast/statement/declaration.rs | 313 ++++ .../if_node/mod.rs => statement/if.rs} | 62 +- .../mod.rs => statement/iteration/break.rs} | 55 +- .../iteration/continue.rs} | 19 +- .../iteration/do_while_loop.rs} | 35 +- .../iteration/for_in_loop.rs} | 41 +- .../ast/statement/iteration/for_loop.rs | 215 +++ .../iteration/for_of_loop.rs} | 40 +- .../src/syntax/ast/statement/iteration/mod.rs | 78 + .../{node => statement}/iteration/tests.rs | 24 +- .../iteration/while_loop.rs} | 38 +- boa_engine/src/syntax/ast/statement/mod.rs | 393 +++++ .../return_smt/mod.rs => statement/return.rs} | 50 +- .../{node => statement}/statement_list/mod.rs | 138 +- .../statement_list/tests.rs | 0 .../ast/{node => statement}/switch/mod.rs | 73 +- .../ast/{node => statement}/switch/tests.rs | 2 +- .../{node/throw/mod.rs => statement/throw.rs} | 45 +- .../{node/try_node => statement/try}/mod.rs | 96 +- .../{node/try_node => statement/try}/tests.rs | 6 +- boa_engine/src/syntax/parser/error.rs | 4 +- .../expression/assignment/arrow_function.rs | 35 +- .../expression/assignment/conditional.rs | 8 +- .../expression/assignment/exponentiation.rs | 16 +- .../parser/expression/assignment/mod.rs | 54 +- .../parser/expression/assignment/yield.rs | 17 +- .../syntax/parser/expression/await_expr.rs | 12 +- .../syntax/parser/expression/identifiers.rs | 16 +- .../expression/left_hand_side/arguments.rs | 13 +- .../parser/expression/left_hand_side/call.rs | 40 +- .../expression/left_hand_side/member.rs | 58 +- .../parser/expression/left_hand_side/mod.rs | 8 +- .../expression/left_hand_side/template.rs | 25 +- .../parser/expression/left_hand_side/tests.rs | 28 +- .../src/syntax/parser/expression/mod.rs | 57 +- .../primary/array_initializer/mod.rs | 28 +- .../primary/array_initializer/tests.rs | 81 +- .../primary/async_function_expression/mod.rs | 15 +- .../async_function_expression/tests.rs | 49 +- .../primary/async_generator_expression/mod.rs | 15 +- .../async_generator_expression/tests.rs | 49 +- .../primary/class_expression/mod.rs | 16 +- .../primary/function_expression/mod.rs | 14 +- .../primary/function_expression/tests.rs | 57 +- .../primary/generator_expression/mod.rs | 14 +- .../primary/generator_expression/tests.rs | 43 +- .../syntax/parser/expression/primary/mod.rs | 149 +- .../primary/object_initializer/mod.rs | 176 +- .../primary/object_initializer/tests.rs | 288 ++-- .../parser/expression/primary/template/mod.rs | 17 +- .../syntax/parser/expression/primary/tests.rs | 14 +- .../src/syntax/parser/expression/tests.rs | 531 +++--- .../src/syntax/parser/expression/unary.rs | 27 +- .../src/syntax/parser/expression/update.rs | 33 +- boa_engine/src/syntax/parser/function/mod.rs | 91 +- .../src/syntax/parser/function/tests.rs | 340 ++-- boa_engine/src/syntax/parser/mod.rs | 26 +- .../src/syntax/parser/statement/block/mod.rs | 18 +- .../syntax/parser/statement/block/tests.rs | 89 +- .../syntax/parser/statement/break_stm/mod.rs | 10 +- .../parser/statement/break_stm/tests.rs | 70 +- .../parser/statement/continue_stm/mod.rs | 10 +- .../parser/statement/continue_stm/tests.rs | 70 +- .../hoistable/async_function_decl/mod.rs | 15 +- .../hoistable/async_function_decl/tests.rs | 29 +- .../hoistable/async_generator_decl/mod.rs | 14 +- .../hoistable/async_generator_decl/tests.rs | 17 +- .../declaration/hoistable/class_decl/mod.rs | 231 +-- .../declaration/hoistable/class_decl/tests.rs | 71 +- .../hoistable/function_decl/mod.rs | 14 +- .../hoistable/function_decl/tests.rs | 23 +- .../hoistable/generator_decl/mod.rs | 15 +- .../hoistable/generator_decl/tests.rs | 17 +- .../statement/declaration/hoistable/mod.rs | 21 +- .../parser/statement/declaration/lexical.rs | 99 +- .../parser/statement/declaration/mod.rs | 40 +- .../parser/statement/declaration/tests.rs | 150 +- .../syntax/parser/statement/expression/mod.rs | 8 +- .../src/syntax/parser/statement/if_stm/mod.rs | 21 +- .../syntax/parser/statement/if_stm/tests.rs | 18 +- .../statement/iteration/do_while_statement.rs | 12 +- .../statement/iteration/for_statement.rs | 205 +-- .../parser/statement/iteration/tests.rs | 161 +- .../statement/iteration/while_statement.rs | 12 +- .../parser/statement/labelled_stm/mod.rs | 31 +- boa_engine/src/syntax/parser/statement/mod.rs | 252 +-- .../syntax/parser/statement/return_stm/mod.rs | 14 +- .../src/syntax/parser/statement/switch/mod.rs | 24 +- .../syntax/parser/statement/switch/tests.rs | 75 +- .../src/syntax/parser/statement/throw/mod.rs | 10 +- .../syntax/parser/statement/throw/tests.rs | 16 +- .../syntax/parser/statement/try_stm/catch.rs | 39 +- .../parser/statement/try_stm/finally.rs | 12 +- .../syntax/parser/statement/try_stm/mod.rs | 10 +- .../syntax/parser/statement/try_stm/tests.rs | 168 +- .../syntax/parser/statement/variable/mod.rs | 31 +- boa_engine/src/syntax/parser/tests.rs | 281 ++-- boa_engine/src/vm/code_block.rs | 2 +- boa_engine/src/vm/opcode.rs | 2 +- 182 files changed, 9548 insertions(+), 10326 deletions(-) create mode 100644 boa_engine/src/syntax/ast/expression/access.rs rename boa_engine/src/syntax/ast/{node/await_expr/mod.rs => expression/await.rs} (50%) create mode 100644 boa_engine/src/syntax/ast/expression/call.rs rename boa_engine/src/syntax/ast/{node/identifier/mod.rs => expression/identifier.rs} (87%) rename boa_engine/src/syntax/ast/{node/array/mod.rs => expression/literal/array.rs} (55%) rename boa_engine/src/syntax/ast/{constant.rs => expression/literal/mod.rs} (89%) create mode 100644 boa_engine/src/syntax/ast/expression/literal/object/mod.rs rename boa_engine/src/syntax/ast/{node => expression/literal}/object/tests.rs (98%) create mode 100644 boa_engine/src/syntax/ast/expression/literal/template.rs create mode 100644 boa_engine/src/syntax/ast/expression/mod.rs rename boa_engine/src/syntax/ast/{node/new/mod.rs => expression/new.rs} (76%) create mode 100644 boa_engine/src/syntax/ast/expression/operator/assign/mod.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/assign/op.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/binary/mod.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/binary/op.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/conditional.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/mod.rs rename boa_engine/src/syntax/ast/{node => expression}/operator/tests.rs (98%) create mode 100644 boa_engine/src/syntax/ast/expression/operator/unary/mod.rs create mode 100644 boa_engine/src/syntax/ast/expression/operator/unary/op.rs create mode 100644 boa_engine/src/syntax/ast/expression/spread.rs create mode 100644 boa_engine/src/syntax/ast/expression/tagged_template.rs rename boa_engine/src/syntax/ast/{node/yield/mod.rs => expression/yield.rs} (69%) rename boa_engine/src/syntax/ast/{node/declaration/arrow_function_decl/mod.rs => function/arrow_function.rs} (56%) rename boa_engine/src/syntax/ast/{node/declaration/async_function_expr/mod.rs => function/async_function.rs} (57%) rename boa_engine/src/syntax/ast/{node/declaration/async_generator_expr/mod.rs => function/async_generator.rs} (60%) create mode 100644 boa_engine/src/syntax/ast/function/class.rs rename boa_engine/src/syntax/ast/{node/declaration/generator_expr/mod.rs => function/generator.rs} (60%) create mode 100644 boa_engine/src/syntax/ast/function/mod.rs rename boa_engine/src/syntax/ast/{node => function}/parameters.rs (91%) delete mode 100644 boa_engine/src/syntax/ast/node/array/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/await_expr/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/block/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/call/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/call/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/conditional/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/conditional/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/class_decl/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/class_decl/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/declaration/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/get_field/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/get_private_field/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/get_super_field/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/field/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/iteration/break_node/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/iteration/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/new/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/operator/assign/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/operator/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/return_smt/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/spread/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/spread/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/super_call/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/template/mod.rs delete mode 100644 boa_engine/src/syntax/ast/node/template/tests.rs delete mode 100644 boa_engine/src/syntax/ast/node/throw/tests.rs delete mode 100644 boa_engine/src/syntax/ast/op.rs create mode 100644 boa_engine/src/syntax/ast/pattern.rs rename boa_engine/src/syntax/ast/{node/object/mod.rs => property.rs} (57%) rename boa_engine/src/syntax/ast/{node/block/mod.rs => statement/block.rs} (71%) create mode 100644 boa_engine/src/syntax/ast/statement/declaration.rs rename boa_engine/src/syntax/ast/{node/conditional/if_node/mod.rs => statement/if.rs} (65%) rename boa_engine/src/syntax/ast/{node/iteration/break_node/mod.rs => statement/iteration/break.rs} (61%) rename boa_engine/src/syntax/ast/{node/iteration/continue_node/mod.rs => statement/iteration/continue.rs} (80%) rename boa_engine/src/syntax/ast/{node/iteration/do_while_loop/mod.rs => statement/iteration/do_while_loop.rs} (71%) rename boa_engine/src/syntax/ast/{node/iteration/for_in_loop/mod.rs => statement/iteration/for_in_loop.rs} (61%) create mode 100644 boa_engine/src/syntax/ast/statement/iteration/for_loop.rs rename boa_engine/src/syntax/ast/{node/iteration/for_of_loop/mod.rs => statement/iteration/for_of_loop.rs} (69%) create mode 100644 boa_engine/src/syntax/ast/statement/iteration/mod.rs rename boa_engine/src/syntax/ast/{node => statement}/iteration/tests.rs (97%) rename boa_engine/src/syntax/ast/{node/iteration/while_loop/mod.rs => statement/iteration/while_loop.rs} (68%) create mode 100644 boa_engine/src/syntax/ast/statement/mod.rs rename boa_engine/src/syntax/ast/{node/return_smt/mod.rs => statement/return.rs} (64%) rename boa_engine/src/syntax/ast/{node => statement}/statement_list/mod.rs (50%) rename boa_engine/src/syntax/ast/{node => statement}/statement_list/tests.rs (100%) rename boa_engine/src/syntax/ast/{node => statement}/switch/mod.rs (66%) rename boa_engine/src/syntax/ast/{node => statement}/switch/tests.rs (99%) rename boa_engine/src/syntax/ast/{node/throw/mod.rs => statement/throw.rs} (60%) rename boa_engine/src/syntax/ast/{node/try_node => statement/try}/mod.rs (62%) rename boa_engine/src/syntax/ast/{node/try_node => statement/try}/tests.rs (97%) diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index cd01b629900..8d825fd4b95 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -59,7 +59,7 @@ rustdoc::missing_doc_code_examples )] -use boa_engine::{syntax::ast::node::StatementList, Context}; +use boa_engine::{syntax::ast::statement::StatementList, Context}; use clap::{ArgEnum, Parser}; use colored::{Color, Colorize}; use rustyline::{config::Config, error::ReadlineError, EditMode, Editor}; diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index a511b683ad1..d1da15801f5 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -102,7 +102,7 @@ impl Eval { // Error if any var declaration in the eval code already exists as a let/const declaration in the current running environment. let mut vars = FxHashSet::default(); - body.var_declared_names_new(&mut vars); + body.var_declared_names(&mut vars); if let Some(name) = context .realm .environments diff --git a/boa_engine/src/builtins/function/arguments.rs b/boa_engine/src/builtins/function/arguments.rs index ab1626a87c3..798364c1612 100644 --- a/boa_engine/src/builtins/function/arguments.rs +++ b/boa_engine/src/builtins/function/arguments.rs @@ -3,7 +3,7 @@ use crate::{ object::{JsObject, ObjectData}, property::PropertyDescriptor, symbol::{self, WellKnownSymbols}, - syntax::ast::node::FormalParameterList, + syntax::ast::function::FormalParameterList, Context, JsValue, }; use boa_gc::{Finalize, Gc, Trace}; diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 9b286b35246..149f402f4ad 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -13,7 +13,7 @@ use crate::{ builtins::{BuiltIn, JsArgs}, - bytecompiler::{FunctionCompiler, FunctionKind}, + bytecompiler::FunctionCompiler, context::intrinsics::StandardConstructors, environments::DeclarativeEnvironmentStack, js_string, @@ -25,10 +25,7 @@ use crate::{ property::{Attribute, PropertyDescriptor, PropertyKey}, string::utf16, symbol::WellKnownSymbols, - syntax::{ - ast::node::{FormalParameterList, StatementList}, - Parser, - }, + syntax::{ast::function::FormalParameterList, ast::statement::StatementList, Parser}, value::IntegerOrInfinity, Context, JsResult, JsString, JsValue, }; @@ -605,7 +602,6 @@ impl BuiltInFunctionObject { .name(Sym::ANONYMOUS) .generator(generator) .r#async(r#async) - .kind(FunctionKind::Expression) .compile(¶meters, &body, context)?; let environments = context.realm.environments.pop_to_global(); @@ -623,7 +619,6 @@ impl BuiltInFunctionObject { let code = FunctionCompiler::new() .name(Sym::ANONYMOUS) .generator(true) - .kind(FunctionKind::Expression) .compile( &FormalParameterList::empty(), &StatementList::default(), @@ -637,14 +632,11 @@ impl BuiltInFunctionObject { Ok(function_object) } else { - let code = FunctionCompiler::new() - .name(Sym::ANONYMOUS) - .kind(FunctionKind::Expression) - .compile( - &FormalParameterList::empty(), - &StatementList::default(), - context, - )?; + let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile( + &FormalParameterList::empty(), + &StatementList::default(), + context, + )?; let environments = context.realm.environments.pop_to_global(); let function_object = diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index 711e23aed02..1203c7d1204 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -1,7 +1,10 @@ use crate::{ builtins::function::ThisMode, - bytecompiler::{ByteCompiler, FunctionKind}, - syntax::ast::node::{Declaration, FormalParameterList, StatementList}, + bytecompiler::ByteCompiler, + syntax::ast::{ + function::FormalParameterList, + statement::{declaration::Binding, StatementList}, + }, vm::{BindingOpcode, CodeBlock, Opcode}, Context, JsResult, }; @@ -11,12 +14,13 @@ use rustc_hash::FxHashMap; /// `FunctionCompiler` is used to compile AST functions to bytecode. #[derive(Debug, Clone, Copy)] +#[allow(clippy::struct_excessive_bools)] pub(crate) struct FunctionCompiler { name: Sym, generator: bool, r#async: bool, strict: bool, - kind: FunctionKind, + arrow: bool, } impl FunctionCompiler { @@ -28,7 +32,7 @@ impl FunctionCompiler { generator: false, r#async: false, strict: false, - kind: FunctionKind::Declaration, + arrow: false, } } @@ -45,6 +49,12 @@ impl FunctionCompiler { self } + /// Indicate if the function is an arrow function. + #[inline] + pub(crate) fn arrow(mut self, arrow: bool) -> Self { + self.arrow = arrow; + self + } /// Indicate if the function is a generator function. #[inline] pub(crate) fn generator(mut self, generator: bool) -> Self { @@ -66,13 +76,6 @@ impl FunctionCompiler { self } - /// Indicate if the function is a declaration, expression or arrow function. - #[inline] - pub(crate) fn kind(mut self, kind: FunctionKind) -> Self { - self.kind = kind; - self - } - /// Compile a function statement list and it's parameters into bytecode. pub(crate) fn compile( mut self, @@ -85,7 +88,7 @@ impl FunctionCompiler { let length = parameters.length(); let mut code = CodeBlock::new(self.name, length, self.strict); - if self.kind == FunctionKind::Arrow { + if self.arrow { code.this_mode = ThisMode::Lexical; } @@ -106,7 +109,7 @@ impl FunctionCompiler { // - If the parameter list does not contain `arguments` (10.2.11.17) // Note: This following just means, that we add an extra environment for the arguments. // - If there are default parameters or if lexical names and function names do not contain `arguments` (10.2.11.18) - if !(self.kind == FunctionKind::Arrow) && !parameters.has_arguments() { + if !(self.arrow) && !parameters.has_arguments() { compiler .context .create_mutable_binding(Sym::ARGUMENTS, false); @@ -122,17 +125,18 @@ impl FunctionCompiler { compiler.emit_opcode(Opcode::RestParameterInit); } - match parameter.declaration() { - Declaration::Identifier { ident, .. } => { + if let Some(init) = parameter.declaration().init() { + let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); + compiler.compile_expr(init, true)?; + compiler.patch_jump(skip); + } + + match parameter.declaration().binding() { + Binding::Identifier(ident) => { compiler.context.create_mutable_binding(ident.sym(), false); - if let Some(init) = parameter.declaration().init() { - let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - compiler.compile_expr(init, true)?; - compiler.patch_jump(skip); - } compiler.emit_binding(BindingOpcode::InitArg, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { compiler.context.create_mutable_binding(ident, false); } @@ -161,8 +165,8 @@ impl FunctionCompiler { compiler.emit_opcode(Opcode::Yield); } - compiler.create_declarations(body.items())?; - compiler.compile_statement_list(body.items(), false)?; + compiler.create_decls(body.statements())?; + compiler.compile_statement_list(body.statements(), false)?; if let Some(env_label) = env_label { let (num_bindings, compile_environment) = diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 48c278f7b50..50d94064714 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -3,19 +3,28 @@ mod function; use crate::{ environments::{BindingLocator, CompileTimeEnvironment}, syntax::ast::{ - node::{ - declaration::{ - class_decl::ClassElement, BindingPatternTypeArray, BindingPatternTypeObject, - DeclarationPattern, + expression::{ + access::{PrivatePropertyAccess, PropertyAccess, PropertyAccessField}, + literal::{self, TemplateElement}, + operator::{ + assign::{op::AssignOp, AssignTarget}, + binary::op::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, + unary::op::UnaryOp, }, - iteration::IterableLoopInitializer, - object::{MethodDefinition, PropertyDefinition, PropertyName}, - operator::assign::AssignTarget, - template::TemplateElement, - Class, Declaration, GetConstField, GetField, GetSuperField, + Call, New, }, - op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, - Const, Node, + function::{ + ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList, + Function, Generator, + }, + pattern::{Pattern, PatternArrayElement, PatternObjectElement}, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + statement::{ + declaration::{Binding, DeclarationList}, + iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, + StatementList, + }, + Expression, Statement, }, vm::{BindingOpcode, CodeBlock, Opcode}, Context, JsBigInt, JsResult, JsString, JsValue, @@ -27,6 +36,111 @@ use std::mem::size_of; pub(crate) use function::FunctionCompiler; +/// Describes how a node has been defined in the source code. +#[derive(Debug, Clone, Copy, PartialEq)] +enum NodeKind { + Declaration, + Expression, +} + +/// Describes the type of a function. +#[derive(Debug, Clone, Copy, PartialEq)] +enum FunctionKind { + Ordinary, + Arrow, + Async, + Generator, + AsyncGenerator, +} + +/// Describes the complete specification of a function node. +#[derive(Debug, Clone, Copy, PartialEq)] +struct FunctionSpec<'a> { + kind: FunctionKind, + name: Option, + parameters: &'a FormalParameterList, + body: &'a StatementList, +} + +impl<'a> FunctionSpec<'a> { + #[inline] + fn is_arrow(&self) -> bool { + self.kind == FunctionKind::Arrow + } + #[inline] + fn is_async(&self) -> bool { + matches!( + self.kind, + FunctionKind::Async | FunctionKind::AsyncGenerator + ) + } + #[inline] + fn is_generator(&self) -> bool { + matches!( + self.kind, + FunctionKind::Generator | FunctionKind::AsyncGenerator + ) + } +} + +impl<'a> From<&'a Function> for FunctionSpec<'a> { + fn from(function: &'a Function) -> Self { + FunctionSpec { + kind: FunctionKind::Ordinary, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + } + } +} +impl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> { + fn from(function: &'a ArrowFunction) -> Self { + FunctionSpec { + kind: FunctionKind::Arrow, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + } + } +} +impl<'a> From<&'a AsyncFunction> for FunctionSpec<'a> { + fn from(function: &'a AsyncFunction) -> Self { + FunctionSpec { + kind: FunctionKind::Async, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + } + } +} +impl<'a> From<&'a Generator> for FunctionSpec<'a> { + fn from(function: &'a Generator) -> Self { + FunctionSpec { + kind: FunctionKind::Generator, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + } + } +} +impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> { + fn from(function: &'a AsyncGenerator) -> Self { + FunctionSpec { + kind: FunctionKind::AsyncGenerator, + name: function.name(), + parameters: function.parameters(), + body: function.body(), + } + } +} + +/// Represents a callable expression, like `f()` or `new Cl()` +#[derive(Debug, Clone, Copy)] +enum Callable<'a> { + Call(&'a Call), + New(&'a New), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum Literal { String(JsString), @@ -63,8 +177,9 @@ enum JumpControlInfoKind { #[derive(Debug, Clone, Copy)] enum Access<'a> { Variable { name: Sym }, - ByName { node: &'a GetConstField }, - ByValue { node: &'a GetField }, + Property { access: &'a PropertyAccess }, + PrivateProperty { access: &'a PrivatePropertyAccess }, + SuperProperty { field: &'a PropertyAccessField }, This, } @@ -523,12 +638,11 @@ impl<'b> ByteCompiler<'b> { } #[inline] - fn compile_access(node: &Node) -> Option> { - match node { - Node::Identifier(name) => Some(Access::Variable { name: name.sym() }), - Node::GetConstField(node) => Some(Access::ByName { node }), - Node::GetField(node) => Some(Access::ByValue { node }), - Node::This => Some(Access::This), + fn compile_access(expr: &Expression) -> Option> { + match expr { + Expression::Identifier(name) => Some(Access::Variable { name: name.sym() }), + Expression::PropertyAccess(access) => Some(Access::Property { access }), + Expression::This => Some(Access::This), _ => None, } } @@ -541,16 +655,35 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetName, &[index]); } - Access::ByName { node } => { - let index = self.get_or_insert_name(node.field()); - self.compile_expr(node.obj(), true)?; - self.emit(Opcode::GetPropertyByName, &[index]); - } - Access::ByValue { node } => { - self.compile_expr(node.field(), true)?; - self.compile_expr(node.obj(), true)?; - self.emit(Opcode::GetPropertyByValue, &[]); + Access::Property { access } => match access.field() { + PropertyAccessField::Const(name) => { + let index = self.get_or_insert_name(*name); + self.compile_expr(access.target(), true)?; + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::GetPropertyByValue, &[]); + } + }, + Access::PrivateProperty { access } => { + let index = self.get_or_insert_name(access.field()); + self.compile_expr(access.target(), true)?; + self.emit(Opcode::GetPrivateField, &[index]); } + Access::SuperProperty { field } => match field { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name(*field); + self.emit_opcode(Opcode::Super); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(&**expr, true)?; + self.emit_opcode(Opcode::Super); + self.emit_opcode(Opcode::GetPropertyByValue); + } + }, Access::This => { self.emit(Opcode::This, &[]); } @@ -566,7 +699,7 @@ impl<'b> ByteCompiler<'b> { fn access_set( &mut self, access: Access<'_>, - expr: Option<&Node>, + expr: Option<&Expression>, use_expr: bool, ) -> JsResult<()> { if let Some(expr) = expr { @@ -583,23 +716,32 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_binding(binding); self.emit(Opcode::SetName, &[index]); } - Access::ByName { node } => { - self.compile_expr(node.obj(), true)?; - let index = self.get_or_insert_name(node.field()); - self.emit(Opcode::SetPropertyByName, &[index]); - } - Access::ByValue { node } => { - self.compile_expr(node.field(), true)?; - self.compile_expr(node.obj(), true)?; - self.emit(Opcode::SetPropertyByValue, &[]); + Access::Property { access } => match access.field() { + PropertyAccessField::Const(name) => { + self.compile_expr(access.target(), true)?; + let index = self.get_or_insert_name(*name); + self.emit(Opcode::SetPropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::SetPropertyByValue, &[]); + } + }, + Access::PrivateProperty { access } => { + self.compile_expr(access.target(), true)?; + self.emit_opcode(Opcode::Swap); + let index = self.get_or_insert_name(access.field()); + self.emit(Opcode::AssignPrivateField, &[index]); } - Access::This => todo!("access_set 'this'"), + Access::SuperProperty { field: _field } => todo!("access_set `super`"), + Access::This => todo!("access_set `this`"), } Ok(()) } #[inline] - pub fn compile_statement_list(&mut self, list: &[Node], use_expr: bool) -> JsResult<()> { + pub fn compile_statement_list(&mut self, list: &[Statement], use_expr: bool) -> JsResult<()> { if let Some((last, items)) = list.split_last() { for node in items { self.compile_stmt(node, false)?; @@ -613,14 +755,14 @@ impl<'b> ByteCompiler<'b> { #[inline] pub(crate) fn compile_statement_list_with_new_declarative( &mut self, - list: &[Node], + list: &[Statement], use_expr: bool, strict: bool, ) -> JsResult<()> { self.context.push_compile_time_environment(strict); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(list)?; + self.create_decls(list)?; if let Some((last, items)) = list.split_last() { for node in items { @@ -639,27 +781,29 @@ impl<'b> ByteCompiler<'b> { } #[inline] - pub fn compile_expr(&mut self, expr: &Node, use_expr: bool) -> JsResult<()> { + pub fn compile_expr(&mut self, expr: &Expression, use_expr: bool) -> JsResult<()> { match expr { - Node::Const(c) => { - match c { - Const::String(v) => self.emit_push_literal(Literal::String( + Expression::Literal(lit) => { + match lit { + literal::Literal::String(v) => self.emit_push_literal(Literal::String( self.interner().resolve_expect(*v).into_common(false), )), - Const::Int(v) => self.emit_push_integer(*v), - Const::Num(v) => self.emit_push_rational(*v), - Const::BigInt(v) => self.emit_push_literal(Literal::BigInt(v.clone().into())), - Const::Bool(true) => self.emit(Opcode::PushTrue, &[]), - Const::Bool(false) => self.emit(Opcode::PushFalse, &[]), - Const::Null => self.emit(Opcode::PushNull, &[]), - Const::Undefined => self.emit(Opcode::PushUndefined, &[]), + literal::Literal::Int(v) => self.emit_push_integer(*v), + literal::Literal::Num(v) => self.emit_push_rational(*v), + literal::Literal::BigInt(v) => { + self.emit_push_literal(Literal::BigInt(v.clone().into())); + } + literal::Literal::Bool(true) => self.emit(Opcode::PushTrue, &[]), + literal::Literal::Bool(false) => self.emit(Opcode::PushFalse, &[]), + literal::Literal::Null => self.emit(Opcode::PushNull, &[]), + literal::Literal::Undefined => self.emit(Opcode::PushUndefined, &[]), } if !use_expr { self.emit(Opcode::Pop, &[]); } } - Node::UnaryOp(unary) => { + Expression::Unary(unary) => { let opcode = match unary.op() { UnaryOp::IncrementPre => { self.compile_expr(unary.target(), true)?; @@ -708,20 +852,23 @@ impl<'b> ByteCompiler<'b> { None } UnaryOp::Delete => match unary.target() { - Node::GetConstField(ref get_const_field) => { - let index = self.get_or_insert_name(get_const_field.field()); - self.compile_expr(get_const_field.obj(), true)?; - self.emit(Opcode::DeletePropertyByName, &[index]); - None - } - Node::GetField(ref get_field) => { - self.compile_expr(get_field.field(), true)?; - self.compile_expr(get_field.obj(), true)?; - self.emit(Opcode::DeletePropertyByValue, &[]); + Expression::PropertyAccess(ref access) => { + match access.field() { + PropertyAccessField::Const(name) => { + let index = self.get_or_insert_name(*name); + self.compile_expr(access.target(), true)?; + self.emit(Opcode::DeletePropertyByName, &[index]); + } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true)?; + self.compile_expr(access.target(), true)?; + self.emit(Opcode::DeletePropertyByValue, &[]); + } + } None } // TODO: implement delete on references. - Node::Identifier(_) => { + Expression::Identifier(_) => { self.emit(Opcode::PushFalse, &[]); None } @@ -736,7 +883,7 @@ impl<'b> ByteCompiler<'b> { UnaryOp::Tilde => Some(Opcode::BitNot), UnaryOp::TypeOf => { match &unary.target() { - Node::Identifier(identifier) => { + Expression::Identifier(identifier) => { let binding = self.context.get_binding_value(identifier.sym()); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetNameOrUndefined, &[index]); @@ -758,71 +905,73 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - Node::BinOp(binary) => { + Expression::Binary(binary) => { self.compile_expr(binary.lhs(), true)?; match binary.op() { - BinOp::Num(op) => { + BinaryOp::Arithmetic(op) => { self.compile_expr(binary.rhs(), true)?; match op { - NumOp::Add => self.emit_opcode(Opcode::Add), - NumOp::Sub => self.emit_opcode(Opcode::Sub), - NumOp::Div => self.emit_opcode(Opcode::Div), - NumOp::Mul => self.emit_opcode(Opcode::Mul), - NumOp::Exp => self.emit_opcode(Opcode::Pow), - NumOp::Mod => self.emit_opcode(Opcode::Mod), + ArithmeticOp::Add => self.emit_opcode(Opcode::Add), + ArithmeticOp::Sub => self.emit_opcode(Opcode::Sub), + ArithmeticOp::Div => self.emit_opcode(Opcode::Div), + ArithmeticOp::Mul => self.emit_opcode(Opcode::Mul), + ArithmeticOp::Exp => self.emit_opcode(Opcode::Pow), + ArithmeticOp::Mod => self.emit_opcode(Opcode::Mod), } if !use_expr { self.emit(Opcode::Pop, &[]); } } - BinOp::Bit(op) => { + BinaryOp::Bitwise(op) => { self.compile_expr(binary.rhs(), true)?; match op { - BitOp::And => self.emit_opcode(Opcode::BitAnd), - BitOp::Or => self.emit_opcode(Opcode::BitOr), - BitOp::Xor => self.emit_opcode(Opcode::BitXor), - BitOp::Shl => self.emit_opcode(Opcode::ShiftLeft), - BitOp::Shr => self.emit_opcode(Opcode::ShiftRight), - BitOp::UShr => self.emit_opcode(Opcode::UnsignedShiftRight), + BitwiseOp::And => self.emit_opcode(Opcode::BitAnd), + BitwiseOp::Or => self.emit_opcode(Opcode::BitOr), + BitwiseOp::Xor => self.emit_opcode(Opcode::BitXor), + BitwiseOp::Shl => self.emit_opcode(Opcode::ShiftLeft), + BitwiseOp::Shr => self.emit_opcode(Opcode::ShiftRight), + BitwiseOp::UShr => self.emit_opcode(Opcode::UnsignedShiftRight), } if !use_expr { self.emit(Opcode::Pop, &[]); } } - BinOp::Comp(op) => { + BinaryOp::Relational(op) => { self.compile_expr(binary.rhs(), true)?; match op { - CompOp::Equal => self.emit_opcode(Opcode::Eq), - CompOp::NotEqual => self.emit_opcode(Opcode::NotEq), - CompOp::StrictEqual => self.emit_opcode(Opcode::StrictEq), - CompOp::StrictNotEqual => self.emit_opcode(Opcode::StrictNotEq), - CompOp::GreaterThan => self.emit_opcode(Opcode::GreaterThan), - CompOp::GreaterThanOrEqual => self.emit_opcode(Opcode::GreaterThanOrEq), - CompOp::LessThan => self.emit_opcode(Opcode::LessThan), - CompOp::LessThanOrEqual => self.emit_opcode(Opcode::LessThanOrEq), - CompOp::In => self.emit_opcode(Opcode::In), - CompOp::InstanceOf => self.emit_opcode(Opcode::InstanceOf), + RelationalOp::Equal => self.emit_opcode(Opcode::Eq), + RelationalOp::NotEqual => self.emit_opcode(Opcode::NotEq), + RelationalOp::StrictEqual => self.emit_opcode(Opcode::StrictEq), + RelationalOp::StrictNotEqual => self.emit_opcode(Opcode::StrictNotEq), + RelationalOp::GreaterThan => self.emit_opcode(Opcode::GreaterThan), + RelationalOp::GreaterThanOrEqual => { + self.emit_opcode(Opcode::GreaterThanOrEq); + } + RelationalOp::LessThan => self.emit_opcode(Opcode::LessThan), + RelationalOp::LessThanOrEqual => self.emit_opcode(Opcode::LessThanOrEq), + RelationalOp::In => self.emit_opcode(Opcode::In), + RelationalOp::InstanceOf => self.emit_opcode(Opcode::InstanceOf), } if !use_expr { self.emit(Opcode::Pop, &[]); } } - BinOp::Log(op) => { + BinaryOp::Logical(op) => { match op { - LogOp::And => { + LogicalOp::And => { let exit = self.emit_opcode_with_operand(Opcode::LogicalAnd); self.compile_expr(binary.rhs(), true)?; self.patch_jump(exit); } - LogOp::Or => { + LogicalOp::Or => { let exit = self.emit_opcode_with_operand(Opcode::LogicalOr); self.compile_expr(binary.rhs(), true)?; self.patch_jump(exit); } - LogOp::Coalesce => { + LogicalOp::Coalesce => { let exit = self.emit_opcode_with_operand(Opcode::Coalesce); self.compile_expr(binary.rhs(), true)?; self.patch_jump(exit); @@ -833,72 +982,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - BinOp::Assign(op) => { - let opcode = match op { - AssignOp::Add => Some(Opcode::Add), - AssignOp::Sub => Some(Opcode::Sub), - AssignOp::Mul => Some(Opcode::Mul), - AssignOp::Div => Some(Opcode::Div), - AssignOp::Mod => Some(Opcode::Mod), - AssignOp::Exp => Some(Opcode::Pow), - AssignOp::And => Some(Opcode::BitAnd), - AssignOp::Or => Some(Opcode::BitOr), - AssignOp::Xor => Some(Opcode::BitXor), - AssignOp::Shl => Some(Opcode::ShiftLeft), - AssignOp::Shr => Some(Opcode::ShiftRight), - AssignOp::Ushr => Some(Opcode::UnsignedShiftRight), - AssignOp::BoolAnd => { - let exit = self.emit_opcode_with_operand(Opcode::LogicalAnd); - self.compile_expr(binary.rhs(), true)?; - let access = - Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) - })?; - self.access_set(access, None, use_expr)?; - self.patch_jump(exit); - None - } - AssignOp::BoolOr => { - let exit = self.emit_opcode_with_operand(Opcode::LogicalOr); - self.compile_expr(binary.rhs(), true)?; - let access = - Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) - })?; - self.access_set(access, None, use_expr)?; - self.patch_jump(exit); - None - } - AssignOp::Coalesce => { - let exit = self.emit_opcode_with_operand(Opcode::Coalesce); - self.compile_expr(binary.rhs(), true)?; - let access = - Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context.construct_syntax_error( - "Invalid left-hand side in assignment", - ) - })?; - self.access_set(access, None, use_expr)?; - self.patch_jump(exit); - None - } - }; - - if let Some(opcode) = opcode { - self.compile_expr(binary.rhs(), true)?; - self.emit(opcode, &[]); - let access = Self::compile_access(binary.lhs()).ok_or_else(|| { - self.context - .construct_syntax_error("Invalid left-hand side in assignment") - })?; - self.access_set(access, None, use_expr)?; - } - } - BinOp::Comma => { + BinaryOp::Comma => { self.emit(Opcode::Pop, &[]); self.compile_expr(binary.rhs(), true)?; @@ -908,7 +992,92 @@ impl<'b> ByteCompiler<'b> { } } } - Node::Object(object) => { + Expression::Assign(assign) if assign.op() == AssignOp::Assign => match assign.lhs() { + AssignTarget::Identifier(name) => self.access_set( + Access::Variable { name: name.sym() }, + Some(assign.rhs()), + use_expr, + )?, + AssignTarget::PrivateProperty(access) => self.access_set( + Access::PrivateProperty { access }, + Some(assign.rhs()), + use_expr, + )?, + AssignTarget::Property(access) => { + self.access_set(Access::Property { access }, Some(assign.rhs()), use_expr)?; + } + AssignTarget::SuperProperty(access) => { + self.access_set( + Access::SuperProperty { + field: access.field(), + }, + Some(assign.rhs()), + use_expr, + )?; + } + AssignTarget::Pattern(pattern) => { + self.compile_expr(assign.rhs(), true)?; + if use_expr { + self.emit_opcode(Opcode::Dup); + } + self.compile_declaration_pattern(pattern, BindingOpcode::SetName)?; + } + }, + Expression::Assign(assign) => { + let access = match assign.lhs() { + AssignTarget::Identifier(name) => Access::Variable { name: name.sym() }, + AssignTarget::Property(access) => Access::Property { access }, + AssignTarget::PrivateProperty(access) => Access::PrivateProperty { access }, + AssignTarget::SuperProperty(access) => Access::SuperProperty { + field: access.field(), + }, + AssignTarget::Pattern(_) => { + panic!("tried to use an assignment operator on a pattern") + } + }; + self.access_get(access, true)?; + let opcode = match assign.op() { + AssignOp::Assign => unreachable!(), + AssignOp::Add => Opcode::Add, + AssignOp::Sub => Opcode::Sub, + AssignOp::Mul => Opcode::Mul, + AssignOp::Div => Opcode::Div, + AssignOp::Mod => Opcode::Mod, + AssignOp::Exp => Opcode::Pow, + AssignOp::And => Opcode::BitAnd, + AssignOp::Or => Opcode::BitOr, + AssignOp::Xor => Opcode::BitXor, + AssignOp::Shl => Opcode::ShiftLeft, + AssignOp::Shr => Opcode::ShiftRight, + AssignOp::Ushr => Opcode::UnsignedShiftRight, + AssignOp::BoolAnd => { + let exit = self.emit_opcode_with_operand(Opcode::LogicalAnd); + self.compile_expr(assign.rhs(), true)?; + self.access_set(access, None, use_expr)?; + self.patch_jump(exit); + return Ok(()); + } + AssignOp::BoolOr => { + let exit = self.emit_opcode_with_operand(Opcode::LogicalOr); + self.compile_expr(assign.rhs(), true)?; + self.access_set(access, None, use_expr)?; + self.patch_jump(exit); + return Ok(()); + } + AssignOp::Coalesce => { + let exit = self.emit_opcode_with_operand(Opcode::Coalesce); + self.compile_expr(assign.rhs(), true)?; + self.access_set(access, None, use_expr)?; + self.patch_jump(exit); + return Ok(()); + } + }; + + self.compile_expr(assign.rhs(), true)?; + self.emit(opcode, &[]); + self.access_set(access, None, use_expr)?; + } + Expression::ObjectLiteral(object) => { self.emit_opcode(Opcode::PushEmptyObject); for property in object.properties() { self.emit_opcode(Opcode::Dup); @@ -917,102 +1086,102 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_name(*ident); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } - PropertyDefinition::Property(name, node) => match name { + PropertyDefinition::Property(name, expr) => match name { PropertyName::Literal(name) => { - self.compile_stmt(node, true)?; + self.compile_expr(expr, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.compile_stmt(node, true)?; + self.compile_expr(expr, true)?; self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, PropertyDefinition::MethodDefinition(kind, name) => match kind { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPropertyGetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::SetPropertyGetterByValue); } }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPropertySetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::SetPropertySetterByValue); } }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, @@ -1035,72 +1204,22 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - Node::Identifier(name) => { - let access = Access::Variable { name: name.sym() }; - self.access_get(access, use_expr)?; + Expression::Identifier(name) => { + self.access_get(Access::Variable { name: name.sym() }, use_expr)?; } - Node::Assign(assign) => match assign.lhs() { - AssignTarget::Identifier(name) => self.access_set( - Access::Variable { name: name.sym() }, - Some(assign.rhs()), - use_expr, - )?, - AssignTarget::GetPrivateField(node) => { - self.compile_expr(assign.rhs(), true)?; - if use_expr { - self.emit_opcode(Opcode::Dup); - } - self.compile_expr(node.obj(), true)?; - self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(node.field()); - self.emit(Opcode::AssignPrivateField, &[index]); - } - AssignTarget::GetConstField(node) => { - self.access_set(Access::ByName { node }, Some(assign.rhs()), use_expr)?; - } - AssignTarget::GetField(node) => { - self.access_set(Access::ByValue { node }, Some(assign.rhs()), use_expr)?; - } - AssignTarget::DeclarationPattern(pattern) => { - self.compile_expr(assign.rhs(), true)?; - if use_expr { - self.emit_opcode(Opcode::Dup); - } - self.compile_declaration_pattern(pattern, BindingOpcode::SetName)?; - } - }, - Node::GetConstField(node) => { - let access = Access::ByName { node }; - self.access_get(access, use_expr)?; + Expression::PropertyAccess(access) => { + self.access_get(Access::Property { access }, use_expr)?; } - Node::GetPrivateField(node) => { - let index = self.get_or_insert_name(node.field()); - self.compile_expr(node.obj(), true)?; - self.emit(Opcode::GetPrivateField, &[index]); - } - Node::GetField(node) => { - let access = Access::ByValue { node }; - self.access_get(access, use_expr)?; + Expression::PrivatePropertyAccess(access) => { + self.access_get(Access::PrivateProperty { access }, use_expr)?; } - Node::GetSuperField(get_super_field) => match get_super_field { - GetSuperField::Const(field) => { - let index = self.get_or_insert_name(*field); - self.emit_opcode(Opcode::Super); - self.emit(Opcode::GetPropertyByName, &[index]); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } - } - GetSuperField::Expr(expr) => { - self.compile_expr(expr, true)?; - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::GetPropertyByValue); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } - } - }, - Node::ConditionalOp(op) => { + Expression::SuperPropertyAccess(access) => self.access_get( + Access::SuperProperty { + field: access.field(), + }, + use_expr, + )?, + Expression::Conditional(op) => { self.compile_expr(op.cond(), true)?; let jelse = self.jump_if_false(); self.compile_expr(op.if_true(), true)?; @@ -1113,22 +1232,21 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - Node::ArrayDecl(array) => { + Expression::ArrayLiteral(array) => { self.emit_opcode(Opcode::PushNewArray); self.emit_opcode(Opcode::PopOnReturnAdd); for element in array.as_ref() { - if let Node::Empty = element { - self.emit_opcode(Opcode::PushElisionToArray); - continue; - } - - self.compile_expr(element, true)?; - if let Node::Spread(_) = element { - self.emit_opcode(Opcode::InitIterator); - self.emit_opcode(Opcode::PushIteratorToArray); + if let Some(element) = element { + self.compile_expr(element, true)?; + if let Expression::Spread(_) = element { + self.emit_opcode(Opcode::InitIterator); + self.emit_opcode(Opcode::PushIteratorToArray); + } else { + self.emit_opcode(Opcode::PushValueToArray); + } } else { - self.emit_opcode(Opcode::PushValueToArray); + self.emit_opcode(Opcode::PushElisionToArray); } } @@ -1137,14 +1255,28 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - Node::This => { + Expression::This => { self.access_get(Access::This, use_expr)?; } - Node::Spread(spread) => self.compile_expr(spread.val(), true)?, - Node::FunctionExpr(_function) => self.function(expr, use_expr)?, - Node::ArrowFunctionDecl(_function) => self.function(expr, use_expr)?, - Node::Call(_) | Node::New(_) => self.call(expr, use_expr)?, - Node::TemplateLit(template_literal) => { + Expression::Spread(spread) => self.compile_expr(spread.val(), true)?, + Expression::Function(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } + Expression::ArrowFunction(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } + Expression::Generator(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } + Expression::AsyncFunction(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } + Expression::AsyncGenerator(function) => { + self.function(function.into(), NodeKind::Expression, use_expr)?; + } + Expression::Call(call) => self.call(Callable::Call(call), use_expr)?, + Expression::New(new) => self.call(Callable::New(new), use_expr)?, + Expression::TemplateLiteral(template_literal) => { for element in template_literal.elements() { match element { TemplateElement::String(s) => self.emit_push_literal(Literal::String( @@ -1165,7 +1297,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Pop, &[]); } } - Node::AwaitExpr(expr) => { + Expression::Await(expr) => { self.compile_expr(expr.expr(), true)?; self.emit_opcode(Opcode::Await); self.emit_opcode(Opcode::GeneratorNext); @@ -1173,10 +1305,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } } - Node::GeneratorExpr(_) | Node::AsyncFunctionExpr(_) | Node::AsyncGeneratorExpr(_) => { - self.function(expr, use_expr)?; - } - Node::Yield(r#yield) => { + Expression::Yield(r#yield) => { if let Some(expr) = r#yield.expr() { self.compile_expr(expr, true)?; } else { @@ -1214,20 +1343,22 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } } - Node::TaggedTemplate(template) => { + Expression::TaggedTemplate(template) => { match template.tag() { - Node::GetConstField(field) => { - self.compile_expr(field.obj(), true)?; + Expression::PropertyAccess(access) => { + self.compile_expr(access.target(), true)?; self.emit(Opcode::Dup, &[]); - let index = self.get_or_insert_name(field.field()); - self.emit(Opcode::GetPropertyByName, &[index]); - } - Node::GetField(field) => { - self.compile_expr(field.obj(), true)?; - self.emit(Opcode::Dup, &[]); - self.compile_expr(field.field(), true)?; - self.emit(Opcode::Swap, &[]); - self.emit(Opcode::GetPropertyByValue, &[]); + match access.field() { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name(*field); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(field) => { + self.compile_expr(field, true)?; + self.emit(Opcode::Swap, &[]); + self.emit(Opcode::GetPropertyByValue, &[]); + } + } } expr => { self.compile_expr(expr, true)?; @@ -1267,18 +1398,18 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Call, &[(template.exprs().len() + 1) as u32]); } - Node::ClassExpr(class) => self.class(class, true)?, - Node::SuperCall(super_call) => { + Expression::Class(class) => self.class(class, true)?, + Expression::SuperCall(super_call) => { let contains_spread = super_call .args() .iter() - .any(|arg| matches!(arg, Node::Spread(_))); + .any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { self.emit_opcode(Opcode::PushNewArray); for arg in super_call.args() { self.compile_expr(arg, true)?; - if let Node::Spread(_) = arg { + if let Expression::Spread(_) = arg { self.emit_opcode(Opcode::InitIterator); self.emit_opcode(Opcode::PushIteratorToArray); } else { @@ -1301,77 +1432,32 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } } - Node::NewTarget => { + Expression::NewTarget => { if use_expr { self.emit_opcode(Opcode::PushNewTarget); } } - _ => unreachable!(), + // TODO: try to remove this variant somehow + Expression::FormalParameterList(_) => unreachable!(), } Ok(()) } - #[inline] - pub fn compile_stmt(&mut self, node: &Node, use_expr: bool) -> JsResult<()> { - match node { - Node::VarDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { - let ident = ident.sym(); - if let Some(expr) = decl.init() { - self.compile_expr(expr, true)?; - self.emit_binding(BindingOpcode::InitVar, ident); - } else { - self.emit_binding(BindingOpcode::Var, ident); - } - } - Declaration::Pattern(pattern) => { - if let Some(init) = decl.init() { - self.compile_expr(init, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - }; - - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; - } - } - } - } - Node::LetDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { - if let Some(expr) = decl.init() { - self.compile_expr(expr, true)?; - self.emit_binding(BindingOpcode::InitLet, ident.sym()); - } else { - self.emit_binding(BindingOpcode::Let, ident.sym()); - } - } - Declaration::Pattern(pattern) => { - if let Some(init) = decl.init() { - self.compile_expr(init, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - }; - - self.compile_declaration_pattern(pattern, BindingOpcode::InitLet)?; - } - } - } - } - Node::ConstDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { + pub fn compile_decl_list(&mut self, list: &DeclarationList) -> JsResult<()> { + let (init_op, empty_op) = match list { + DeclarationList::Var(_) => (BindingOpcode::InitVar, BindingOpcode::Var), + DeclarationList::Let(_) => (BindingOpcode::InitLet, BindingOpcode::Let), + DeclarationList::Const(decls) => { + for decl in &**decls { + match decl.binding() { + Binding::Identifier(ident) => { let init = decl .init() .expect("const declaration must have initializer"); self.compile_expr(init, true)?; self.emit_binding(BindingOpcode::InitConst, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { if let Some(init) = decl.init() { self.compile_expr(init, true)?; } else { @@ -1382,12 +1468,43 @@ impl<'b> ByteCompiler<'b> { } } } + return Ok(()); } - Node::If(node) => { + }; + for decl in list.as_ref() { + match decl.binding() { + Binding::Identifier(ident) => { + let ident = ident.sym(); + if let Some(expr) = decl.init() { + self.compile_expr(expr, true)?; + self.emit_binding(init_op, ident); + } else { + self.emit_binding(empty_op, ident); + } + } + Binding::Pattern(pattern) => { + if let Some(init) = decl.init() { + self.compile_expr(init, true)?; + } else { + self.emit_opcode(Opcode::PushUndefined); + }; + + self.compile_declaration_pattern(pattern, init_op)?; + } + } + } + Ok(()) + } + + #[inline] + pub fn compile_stmt(&mut self, node: &Statement, use_expr: bool) -> JsResult<()> { + match node { + Statement::DeclarationList(list) => self.compile_decl_list(list)?, + Statement::If(node) => { self.compile_expr(node.cond(), true)?; let jelse = self.jump_if_false(); - if !matches!(node.body(), Node::Block(_)) { + if !matches!(node.body(), Statement::Block(_)) { self.create_decls_from_stmt(node.body())?; } @@ -1400,7 +1517,7 @@ impl<'b> ByteCompiler<'b> { Some(else_body) => { let exit = self.jump(); self.patch_jump(jelse); - if !matches!(else_body, Node::Block(_)) { + if !matches!(else_body, Statement::Block(_)) { self.create_decls_from_stmt(else_body)?; } self.compile_stmt(else_body, false)?; @@ -1408,14 +1525,19 @@ impl<'b> ByteCompiler<'b> { } } } - Node::ForLoop(for_loop) => { + Statement::ForLoop(for_loop) => { self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); if let Some(init) = for_loop.init() { - self.create_decls_from_stmt(init)?; - self.compile_stmt(init, false)?; + match init { + ForLoopInitializer::Expression(expr) => self.compile_expr(expr, false)?, + ForLoopInitializer::DeclarationList(list) => { + self.create_decls_from_decl_list(list); + self.compile_decl_list(list)?; + } + } } self.emit_opcode(Opcode::LoopStart); @@ -1438,7 +1560,7 @@ impl<'b> ByteCompiler<'b> { } let exit = self.jump_if_false(); - if !matches!(for_loop.body(), Node::Block(_)) { + if !matches!(for_loop.body(), Statement::Block(_)) { self.create_decls_from_stmt(for_loop.body())?; } self.compile_stmt(for_loop.body(), false)?; @@ -1456,7 +1578,7 @@ impl<'b> ByteCompiler<'b> { self.patch_jump_with_target(push_env.1, index_compile_environment as u32); self.emit_opcode(Opcode::PopEnvironment); } - Node::ForInLoop(for_in_loop) => { + Statement::ForInLoop(for_in_loop) => { let init_bound_names = for_in_loop.init().bound_names(); if init_bound_names.is_empty() { self.compile_expr(for_in_loop.expr(), true)?; @@ -1492,18 +1614,18 @@ impl<'b> ByteCompiler<'b> { let exit = self.emit_opcode_with_operand(Opcode::ForInLoopNext); match for_in_loop.init() { - IterableLoopInitializer::Identifier(ref ident) => { + IterableLoopInitializer::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), true); let binding = self.context.set_mutable_binding(ident.sym()); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } IterableLoopInitializer::Var(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), true); self.emit_binding(BindingOpcode::InitVar, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_mutable_binding(ident, true); } @@ -1511,11 +1633,11 @@ impl<'b> ByteCompiler<'b> { } }, IterableLoopInitializer::Let(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), false); self.emit_binding(BindingOpcode::InitLet, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_mutable_binding(ident, false); } @@ -1523,23 +1645,17 @@ impl<'b> ByteCompiler<'b> { } }, IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_immutable_binding(ident.sym()); self.emit_binding(BindingOpcode::InitConst, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_immutable_binding(ident); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } }, - IterableLoopInitializer::DeclarationPattern(pattern) => { - for ident in pattern.idents() { - self.context.create_mutable_binding(ident, true); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; - } } self.compile_stmt(for_in_loop.body(), false)?; @@ -1560,7 +1676,7 @@ impl<'b> ByteCompiler<'b> { self.patch_jump(early_exit); } - Node::ForOfLoop(for_of_loop) => { + Statement::ForOfLoop(for_of_loop) => { let init_bound_names = for_of_loop.init().bound_names(); if init_bound_names.is_empty() { self.compile_expr(for_of_loop.iterable(), true)?; @@ -1615,11 +1731,11 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::DefInitVar, &[index]); } IterableLoopInitializer::Var(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), true); self.emit_binding(BindingOpcode::InitVar, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_mutable_binding(ident, true); } @@ -1627,11 +1743,11 @@ impl<'b> ByteCompiler<'b> { } }, IterableLoopInitializer::Let(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), false); self.emit_binding(BindingOpcode::InitLet, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_mutable_binding(ident, false); } @@ -1639,23 +1755,17 @@ impl<'b> ByteCompiler<'b> { } }, IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { + Binding::Identifier(ident) => { self.context.create_immutable_binding(ident.sym()); self.emit_binding(BindingOpcode::InitConst, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_immutable_binding(ident); } self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } }, - IterableLoopInitializer::DeclarationPattern(pattern) => { - for ident in pattern.idents() { - self.context.create_mutable_binding(ident, true); - } - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; - } } self.compile_stmt(for_of_loop.body(), false)?; @@ -1674,13 +1784,13 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::LoopEnd); self.emit_opcode(Opcode::IteratorClose); } - Node::WhileLoop(while_) => { + Statement::WhileLoop(while_) => { self.emit_opcode(Opcode::LoopStart); let start_address = self.next_opcode_location(); self.push_loop_control_info(while_.label(), start_address); self.emit_opcode(Opcode::LoopContinue); - self.compile_expr(while_.cond(), true)?; + self.compile_expr(while_.condition(), true)?; let exit = self.jump_if_false(); self.compile_stmt(while_.body(), false)?; self.emit(Opcode::Jump, &[start_address]); @@ -1689,7 +1799,7 @@ impl<'b> ByteCompiler<'b> { self.pop_loop_control_info(); self.emit_opcode(Opcode::LoopEnd); } - Node::DoWhileLoop(do_while) => { + Statement::DoWhileLoop(do_while) => { self.emit_opcode(Opcode::LoopStart); let initial_label = self.jump(); @@ -1710,7 +1820,7 @@ impl<'b> ByteCompiler<'b> { self.pop_loop_control_info(); self.emit_opcode(Opcode::LoopEnd); } - Node::Continue(node) => { + Statement::Continue(node) => { let next = self.next_opcode_location(); if let Some(info) = self .jump_info @@ -1792,7 +1902,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Jump, &[address]); } } - Node::Break(node) => { + Statement::Break(node) => { let next = self.next_opcode_location(); if let Some(info) = self .jump_info @@ -1844,7 +1954,7 @@ impl<'b> ByteCompiler<'b> { .push(label); } } - Node::Block(block) => { + Statement::Block(block) => { if let Some(label) = block.label() { let next = self.next_opcode_location(); self.push_labelled_block_control_info(label, next); @@ -1853,8 +1963,8 @@ impl<'b> ByteCompiler<'b> { self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(block.items())?; - self.compile_statement_list(block.items(), use_expr)?; + self.create_decls(block.statements())?; + self.compile_statement_list(block.statements(), use_expr)?; let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); let index_compile_environment = self.push_compile_environment(compile_environment); @@ -1867,16 +1977,16 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::PopEnvironment); } - Node::Throw(throw) => { + Statement::Throw(throw) => { self.compile_expr(throw.expr(), true)?; self.emit(Opcode::Throw, &[]); } - Node::Switch(switch) => { + Statement::Switch(switch) => { self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); for case in switch.cases() { - self.create_declarations(case.body().items())?; + self.create_decls(case.body().statements())?; } self.emit_opcode(Opcode::LoopStart); @@ -1894,13 +2004,13 @@ impl<'b> ByteCompiler<'b> { for (label, case) in labels.into_iter().zip(switch.cases()) { self.patch_jump(label); - self.compile_statement_list(case.body().items(), false)?; + self.compile_statement_list(case.body().statements(), false)?; } self.patch_jump(exit); if let Some(body) = switch.default() { - self.create_declarations(body)?; - self.compile_statement_list(body, false)?; + self.create_decls(body.statements())?; + self.compile_statement_list(body.statements(), false)?; } self.pop_switch_control_info(); @@ -1914,8 +2024,19 @@ impl<'b> ByteCompiler<'b> { self.patch_jump_with_target(push_env.1, index_compile_environment as u32); self.emit_opcode(Opcode::PopEnvironment); } - Node::FunctionDecl(_function) => self.function(node, false)?, - Node::Return(ret) => { + Statement::Function(function) => { + self.function(function.into(), NodeKind::Declaration, false)?; + } + Statement::Generator(function) => { + self.function(function.into(), NodeKind::Declaration, false)?; + } + Statement::AsyncFunction(function) => { + self.function(function.into(), NodeKind::Declaration, false)?; + } + Statement::AsyncGenerator(function) => { + self.function(function.into(), NodeKind::Declaration, false)?; + } + Statement::Return(ret) => { if let Some(expr) = ret.expr() { self.compile_expr(expr, true)?; } else { @@ -1923,7 +2044,7 @@ impl<'b> ByteCompiler<'b> { } self.emit(Opcode::Return, &[]); } - Node::Try(t) => { + Statement::Try(t) => { self.push_try_control_info(t.finally().is_some()); let try_start = self.next_opcode_location(); self.emit(Opcode::TryStart, &[Self::DUMMY_ADDRESS, 0]); @@ -1931,8 +2052,8 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(t.block().items())?; - self.compile_statement_list(t.block().items(), use_expr)?; + self.create_decls(t.block().statements())?; + self.compile_statement_list(t.block().statements(), use_expr)?; let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); @@ -1955,13 +2076,13 @@ impl<'b> ByteCompiler<'b> { self.context.push_compile_time_environment(false); let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - if let Some(decl) = catch.parameter() { - match decl { - Declaration::Identifier { ident, .. } => { + if let Some(binding) = catch.parameter() { + match binding { + Binding::Identifier(ident) => { self.context.create_mutable_binding(ident.sym(), false); self.emit_binding(BindingOpcode::InitLet, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { self.context.create_mutable_binding(ident, false); } @@ -1972,8 +2093,8 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } - self.create_declarations(catch.block().items())?; - self.compile_statement_list(catch.block().items(), use_expr)?; + self.create_decls(catch.block().statements())?; + self.compile_statement_list(catch.block().statements(), use_expr)?; let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); @@ -2009,8 +2130,8 @@ impl<'b> ByteCompiler<'b> { let push_env = self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment); - self.create_declarations(finally.items())?; - self.compile_statement_list(finally.items(), false)?; + self.create_decls(finally.statements())?; + self.compile_statement_list(finally.statements(), false)?; let (num_bindings, compile_environment) = self.context.pop_compile_time_environment(); @@ -2026,100 +2147,39 @@ impl<'b> ByteCompiler<'b> { self.pop_try_control_info(None); } } - Node::GeneratorDecl(_) | Node::AsyncFunctionDecl(_) | Node::AsyncGeneratorDecl(_) => { - self.function(node, false)?; - } - Node::ClassDecl(class) => self.class(class, false)?, - Node::Empty => {} - expr => self.compile_expr(expr, use_expr)?, + Statement::Class(class) => self.class(class, false)?, + Statement::Empty => {} + Statement::Expression(expr) => self.compile_expr(expr, use_expr)?, } Ok(()) } /// Compile a function AST Node into bytecode. - pub(crate) fn function(&mut self, function: &Node, use_expr: bool) -> JsResult<()> { - let (kind, name, parameters, body, generator, r#async) = match function { - Node::FunctionDecl(function) => ( - FunctionKind::Declaration, - Some(function.name()), - function.parameters(), - function.body(), - false, - false, - ), - Node::AsyncFunctionDecl(function) => ( - FunctionKind::Declaration, - Some(function.name()), - function.parameters(), - function.body(), - false, - true, - ), - Node::GeneratorDecl(generator) => ( - FunctionKind::Declaration, - Some(generator.name()), - generator.parameters(), - generator.body(), - true, - false, - ), - Node::AsyncGeneratorDecl(generator) => ( - FunctionKind::Declaration, - Some(generator.name()), - generator.parameters(), - generator.body(), - true, - true, - ), - Node::FunctionExpr(function) => ( - FunctionKind::Expression, - function.name(), - function.parameters(), - function.body(), - false, - false, - ), - Node::AsyncFunctionExpr(function) => ( - FunctionKind::Expression, - function.name(), - function.parameters(), - function.body(), - false, - true, - ), - Node::GeneratorExpr(generator) => ( - FunctionKind::Expression, - generator.name(), - generator.parameters(), - generator.body(), - true, - false, - ), - Node::AsyncGeneratorExpr(generator) => ( - FunctionKind::Expression, - generator.name(), - generator.parameters(), - generator.body(), - true, - true, - ), - Node::ArrowFunctionDecl(function) => ( - FunctionKind::Arrow, - function.name(), - function.params(), - function.body(), - false, - false, - ), - _ => unreachable!(), - }; + fn function( + &mut self, + function: FunctionSpec<'_>, + node_kind: NodeKind, + use_expr: bool, + ) -> JsResult<()> { + let (generator, r#async, arrow) = ( + function.is_generator(), + function.is_async(), + function.is_arrow(), + ); + + let FunctionSpec { + name, + parameters, + body, + .. + } = function; let code = FunctionCompiler::new() .name(name) .generator(generator) .r#async(r#async) .strict(self.code_block.strict) - .kind(kind) + .arrow(arrow) .compile(parameters, body, self.context)?; let index = self.code_block.functions.len() as u32; @@ -2135,14 +2195,14 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::GetFunction, &[index]); } - match kind { - FunctionKind::Declaration => { + match node_kind { + NodeKind::Declaration => { self.emit_binding( BindingOpcode::InitVar, name.expect("function declaration must have a name"), ); } - FunctionKind::Expression | FunctionKind::Arrow => { + NodeKind::Expression => { if !use_expr { self.emit(Opcode::Pop, &[]); } @@ -2152,7 +2212,7 @@ impl<'b> ByteCompiler<'b> { Ok(()) } - pub(crate) fn call(&mut self, node: &Node, use_expr: bool) -> JsResult<()> { + fn call(&mut self, callable: Callable<'_>, use_expr: bool) -> JsResult<()> { #[derive(PartialEq)] enum CallKind { CallEval, @@ -2160,56 +2220,57 @@ impl<'b> ByteCompiler<'b> { New, } - let (call, kind) = match node { - Node::Call(call) => match call.expr() { - Node::Identifier(ident) if ident.sym() == Sym::EVAL => (call, CallKind::CallEval), + let (call, kind) = match callable { + Callable::Call(call) => match call.expr() { + Expression::Identifier(ident) if ident.sym() == Sym::EVAL => { + (call, CallKind::CallEval) + } _ => (call, CallKind::Call), }, - Node::New(new) => (new.call(), CallKind::New), - _ => unreachable!(), + Callable::New(new) => (new.call(), CallKind::New), }; match call.expr() { - Node::GetConstField(field) => { - self.compile_expr(field.obj(), true)?; + Expression::PropertyAccess(access) => { + self.compile_expr(access.target(), true)?; if kind == CallKind::Call { self.emit(Opcode::Dup, &[]); } - let index = self.get_or_insert_name(field.field()); - self.emit(Opcode::GetPropertyByName, &[index]); - } - Node::GetField(field) => { - self.compile_expr(field.obj(), true)?; - if kind == CallKind::Call { - self.emit(Opcode::Dup, &[]); + match access.field() { + PropertyAccessField::Const(field) => { + let index = self.get_or_insert_name(*field); + self.emit(Opcode::GetPropertyByName, &[index]); + } + PropertyAccessField::Expr(field) => { + self.compile_expr(field, true)?; + self.emit(Opcode::Swap, &[]); + self.emit(Opcode::GetPropertyByValue, &[]); + } } - self.compile_expr(field.field(), true)?; - self.emit(Opcode::Swap, &[]); - self.emit(Opcode::GetPropertyByValue, &[]); } - Node::GetSuperField(get_super_field) => { + Expression::SuperPropertyAccess(access) => { if kind == CallKind::Call { self.emit_opcode(Opcode::This); } self.emit_opcode(Opcode::Super); - match get_super_field { - GetSuperField::Const(field) => { + match access.field() { + PropertyAccessField::Const(field) => { let index = self.get_or_insert_name(*field); self.emit(Opcode::GetPropertyByName, &[index]); } - GetSuperField::Expr(expr) => { + PropertyAccessField::Expr(expr) => { self.compile_expr(expr, true)?; self.emit_opcode(Opcode::Swap); self.emit_opcode(Opcode::GetPropertyByValue); } } } - Node::GetPrivateField(get_private_field) => { - self.compile_expr(get_private_field.obj(), true)?; + Expression::PrivatePropertyAccess(access) => { + self.compile_expr(access.target(), true)?; if kind == CallKind::Call { self.emit(Opcode::Dup, &[]); } - let index = self.get_or_insert_name(get_private_field.field()); + let index = self.get_or_insert_name(access.field()); self.emit(Opcode::GetPrivateField, &[index]); } expr => { @@ -2221,13 +2282,16 @@ impl<'b> ByteCompiler<'b> { } } - let contains_spread = call.args().iter().any(|arg| matches!(arg, Node::Spread(_))); + let contains_spread = call + .args() + .iter() + .any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { self.emit_opcode(Opcode::PushNewArray); for arg in call.args() { self.compile_expr(arg, true)?; - if let Node::Spread(_) = arg { + if let Expression::Spread(_) = arg { self.emit_opcode(Opcode::InitIterator); self.emit_opcode(Opcode::PushIteratorToArray); } else { @@ -2263,18 +2327,11 @@ impl<'b> ByteCompiler<'b> { #[inline] fn compile_declaration_pattern( &mut self, - pattern: &DeclarationPattern, + pattern: &Pattern, def: BindingOpcode, ) -> JsResult<()> { match pattern { - DeclarationPattern::Object(pattern) => { - let skip_init = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - if let Some(init) = pattern.init() { - self.compile_expr(init, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - } - self.patch_jump(skip_init); + Pattern::Object(pattern) => { self.emit_opcode(Opcode::ValueNotNullOrUndefined); self.emit_opcode(Opcode::RequireObjectCoercible); @@ -2283,9 +2340,9 @@ impl<'b> ByteCompiler<'b> { let rest_exits = pattern.has_rest(); for binding in pattern.bindings() { - use BindingPatternTypeObject::{ - AssignmentGetConstField, AssignmentGetField, AssignmentRestProperty, - BindingPattern, Empty, RestProperty, SingleName, + use PatternObjectElement::{ + AssignmentPropertyAccess, AssignmentRestPropertyAccess, Empty, Pattern, + RestProperty, SingleName, }; match binding { @@ -2294,7 +2351,7 @@ impl<'b> ByteCompiler<'b> { // SingleNameBinding : BindingIdentifier Initializer[opt] SingleName { ident, - property_name, + name: property_name, default_init, } => { self.emit_opcode(Opcode::Dup); @@ -2346,8 +2403,8 @@ impl<'b> ByteCompiler<'b> { ); self.emit_binding(def, *ident); } - AssignmentRestProperty { - get_const_field, + AssignmentRestPropertyAccess { + access, excluded_keys, } => { self.emit_opcode(Opcode::Dup); @@ -2358,63 +2415,15 @@ impl<'b> ByteCompiler<'b> { )); } self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32, 0]); - self.access_set( - Access::ByName { - node: get_const_field, - }, - None, - false, - )?; - } - AssignmentGetConstField { - property_name, - get_const_field, - default_init, - } => { - self.emit_opcode(Opcode::Dup); - match property_name { - PropertyName::Literal(name) => { - let index = self.get_or_insert_name(*name); - self.emit(Opcode::GetPropertyByName, &[index]); - } - PropertyName::Computed(node) => { - self.compile_expr(node, true)?; - self.emit_opcode(Opcode::Swap); - if rest_exits { - self.emit_opcode(Opcode::GetPropertyByValuePush); - } else { - self.emit_opcode(Opcode::GetPropertyByValue); - } - } - } - - if let Some(init) = default_init { - let skip = - self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true)?; - self.patch_jump(skip); - } - - self.access_set( - Access::ByName { - node: get_const_field, - }, - None, - false, - )?; - - if rest_exits && property_name.computed().is_some() { - self.emit_opcode(Opcode::Swap); - additional_excluded_keys_count += 1; - } + self.access_set(Access::Property { access }, None, false)?; } - AssignmentGetField { - property_name, - get_field, + AssignmentPropertyAccess { + name, + access, default_init, } => { self.emit_opcode(Opcode::Dup); - match property_name { + match name { PropertyName::Literal(name) => { let index = self.get_or_insert_name(*name); self.emit(Opcode::GetPropertyByName, &[index]); @@ -2437,20 +2446,20 @@ impl<'b> ByteCompiler<'b> { self.patch_jump(skip); } - self.access_set(Access::ByValue { node: get_field }, None, false)?; + self.access_set(Access::Property { access }, None, false)?; - if rest_exits && property_name.computed().is_some() { + if rest_exits && name.computed().is_some() { self.emit_opcode(Opcode::Swap); additional_excluded_keys_count += 1; } } - BindingPattern { - ident, + Pattern { + name, pattern, default_init, } => { self.emit_opcode(Opcode::Dup); - match ident { + match name { PropertyName::Literal(name) => { let index = self.get_or_insert_name(*name); self.emit(Opcode::GetPropertyByName, &[index]); @@ -2478,21 +2487,14 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); } } - DeclarationPattern::Array(pattern) => { - let skip_init = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - if let Some(init) = pattern.init() { - self.compile_expr(init, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - } - self.patch_jump(skip_init); + Pattern::Array(pattern) => { self.emit_opcode(Opcode::ValueNotNullOrUndefined); self.emit_opcode(Opcode::InitIterator); for binding in pattern.bindings().iter() { - use BindingPatternTypeArray::{ - BindingPattern, BindingPatternRest, Elision, Empty, GetConstField, - GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest, + use PatternArrayElement::{ + Elision, Empty, Pattern, PatternRest, PropertyAccess, PropertyAccessRest, + SingleName, SingleNameRest, }; match binding { @@ -2517,23 +2519,24 @@ impl<'b> ByteCompiler<'b> { } self.emit_binding(def, *ident); } - GetField { get_field } => { + PropertyAccess { access } => { self.emit_opcode(Opcode::IteratorNext); - self.access_set(Access::ByValue { node: get_field }, None, false)?; - } - GetConstField { get_const_field } => { - self.emit_opcode(Opcode::IteratorNext); - self.access_set( - Access::ByName { - node: get_const_field, - }, - None, - false, - )?; + self.access_set(Access::Property { access }, None, false)?; } // BindingElement : BindingPattern Initializer[opt] - BindingPattern { pattern } => { + Pattern { + pattern, + default_init, + } => { self.emit_opcode(Opcode::IteratorNext); + + if let Some(init) = default_init { + let skip = + self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); + self.compile_expr(init, true)?; + self.patch_jump(skip); + } + self.compile_declaration_pattern(pattern, def)?; } // BindingRestElement : ... BindingIdentifier @@ -2541,22 +2544,12 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::IteratorToArray); self.emit_binding(def, *ident); } - GetFieldRest { get_field } => { + PropertyAccessRest { access } => { self.emit_opcode(Opcode::IteratorToArray); - self.access_set(Access::ByValue { node: get_field }, None, false)?; - } - GetConstFieldRest { get_const_field } => { - self.emit_opcode(Opcode::IteratorToArray); - self.access_set( - Access::ByName { - node: get_const_field, - }, - None, - false, - )?; + self.access_set(Access::Property { access }, None, false)?; } // BindingRestElement : ... BindingPattern - BindingPatternRest { pattern } => { + PatternRest { pattern } => { self.emit_opcode(Opcode::IteratorToArray); self.compile_declaration_pattern(pattern, def)?; } @@ -2569,28 +2562,27 @@ impl<'b> ByteCompiler<'b> { Ok(()) } - pub(crate) fn create_declarations(&mut self, stmt_list: &[Node]) -> JsResult<()> { + pub(crate) fn create_decls(&mut self, stmt_list: &[Statement]) -> JsResult<()> { for node in stmt_list { self.create_decls_from_stmt(node)?; } Ok(()) } - pub(crate) fn create_decls_from_stmt(&mut self, node: &Node) -> JsResult { + pub(crate) fn create_decls_from_decl_list(&mut self, list: &DeclarationList) -> bool { let mut has_identifier_argument = false; - - match node { - Node::VarDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { + match list { + DeclarationList::Var(list) => { + for decl in &**list { + match decl.binding() { + Binding::Identifier(ident) => { let ident = ident.sym(); if ident == Sym::ARGUMENTS { has_identifier_argument = true; } self.context.create_mutable_binding(ident, true); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { if ident == Sym::ARGUMENTS { has_identifier_argument = true; @@ -2601,17 +2593,17 @@ impl<'b> ByteCompiler<'b> { } } } - Node::LetDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { + DeclarationList::Let(list) => { + for decl in &**list { + match decl.binding() { + Binding::Identifier(ident) => { let ident = ident.sym(); if ident == Sym::ARGUMENTS { has_identifier_argument = true; } self.context.create_mutable_binding(ident, false); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { if ident == Sym::ARGUMENTS { has_identifier_argument = true; @@ -2622,17 +2614,17 @@ impl<'b> ByteCompiler<'b> { } } } - Node::ConstDeclList(list) => { - for decl in list.as_ref() { - match decl { - Declaration::Identifier { ident, .. } => { + DeclarationList::Const(list) => { + for decl in &**list { + match decl.binding() { + Binding::Identifier(ident) => { let ident = ident.sym(); if ident == Sym::ARGUMENTS { has_identifier_argument = true; } self.context.create_immutable_binding(ident); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { if ident == Sym::ARGUMENTS { has_identifier_argument = true; @@ -2643,55 +2635,63 @@ impl<'b> ByteCompiler<'b> { } } } - Node::ClassDecl(decl) => { - self.context.create_mutable_binding(decl.name(), false); - } - Node::FunctionDecl(decl) => { - let ident = decl.name(); - if ident == Sym::ARGUMENTS { - has_identifier_argument = true; - } + } + has_identifier_argument + } + + pub(crate) fn create_decls_from_stmt(&mut self, node: &Statement) -> JsResult { + match node { + Statement::DeclarationList(list) => Ok(self.create_decls_from_decl_list(list)), + Statement::Class(decl) => { + let ident = decl.name().expect("class declaration must have a name"); + self.context.create_mutable_binding(ident, false); + Ok(false) + } + Statement::Function(decl) => { + let ident = decl.name().expect("function declaration must have a name"); self.context.create_mutable_binding(ident, true); + Ok(ident == Sym::ARGUMENTS) } - Node::GeneratorDecl(decl) => { - let ident = decl.name(); - if ident == Sym::ARGUMENTS { - has_identifier_argument = true; - } + Statement::Generator(decl) => { + let ident = decl.name().expect("generator declaration must have a name"); + self.context.create_mutable_binding(ident, true); + Ok(ident == Sym::ARGUMENTS) } - Node::AsyncFunctionDecl(decl) => { - let ident = decl.name(); - if ident == Sym::ARGUMENTS { - has_identifier_argument = true; - } + Statement::AsyncFunction(decl) => { + let ident = decl + .name() + .expect("async function declaration must have a name"); self.context.create_mutable_binding(ident, true); + Ok(ident == Sym::ARGUMENTS) } - Node::AsyncGeneratorDecl(decl) => { - let ident = decl.name(); - if ident == Sym::ARGUMENTS { - has_identifier_argument = true; - } + Statement::AsyncGenerator(decl) => { + let ident = decl + .name() + .expect("async generator declaration must have a name"); self.context.create_mutable_binding(ident, true); + Ok(ident == Sym::ARGUMENTS) } - Node::DoWhileLoop(do_while_loop) => { - if !matches!(do_while_loop.body(), Node::Block(_)) { + Statement::DoWhileLoop(do_while_loop) => { + if !matches!(do_while_loop.body(), Statement::Block(_)) { self.create_decls_from_stmt(do_while_loop.body())?; } + Ok(false) } - Node::ForInLoop(for_in_loop) => { - if !matches!(for_in_loop.body(), Node::Block(_)) { + Statement::ForInLoop(for_in_loop) => { + if !matches!(for_in_loop.body(), Statement::Block(_)) { self.create_decls_from_stmt(for_in_loop.body())?; } + Ok(false) } - Node::ForOfLoop(for_of_loop) => { - if !matches!(for_of_loop.body(), Node::Block(_)) { + Statement::ForOfLoop(for_of_loop) => { + if !matches!(for_of_loop.body(), Statement::Block(_)) { self.create_decls_from_stmt(for_of_loop.body())?; } + Ok(false) } - _ => {} + _ => Ok(false), } - Ok(has_identifier_argument) } /// This function compiles a class declaration or expression. @@ -2700,7 +2700,7 @@ impl<'b> ByteCompiler<'b> { /// A class declaration binds the resulting class object to it's identifier. /// A class expression leaves the resulting class object on the stack for following operations. fn class(&mut self, class: &Class, expression: bool) -> JsResult<()> { - let code = CodeBlock::new(class.name(), 0, true); + let code = CodeBlock::new(class.name().unwrap_or(Sym::EMPTY_STRING), 0, true); let mut compiler = ByteCompiler { code_block: code, literals_map: FxHashMap::default(), @@ -2728,8 +2728,8 @@ impl<'b> ByteCompiler<'b> { compiler.emit_opcode(Opcode::RestParameterInit); } - match parameter.declaration() { - Declaration::Identifier { ident, .. } => { + match parameter.declaration().binding() { + Binding::Identifier(ident) => { compiler.context.create_mutable_binding(ident.sym(), false); if let Some(init) = parameter.declaration().init() { let skip = @@ -2739,7 +2739,7 @@ impl<'b> ByteCompiler<'b> { } compiler.emit_binding(BindingOpcode::InitArg, ident.sym()); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { for ident in pattern.idents() { compiler.context.create_mutable_binding(ident, false); } @@ -2759,8 +2759,8 @@ impl<'b> ByteCompiler<'b> { } else { None }; - compiler.create_declarations(expr.body().items())?; - compiler.compile_statement_list(expr.body().items(), false)?; + compiler.create_decls(expr.body().statements())?; + compiler.compile_statement_list(expr.body().statements(), false)?; if let Some(env_label) = env_label { let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); @@ -2819,85 +2819,85 @@ impl<'b> ByteCompiler<'b> { match method_definition { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassGetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassGetterByValue); } }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassSetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassSetterByValue); } }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, @@ -2907,32 +2907,32 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); match method_definition { MethodDefinition::Get(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } @@ -2962,7 +2962,7 @@ impl<'b> ByteCompiler<'b> { }; field_compiler.context.push_compile_time_environment(true); if let Some(node) = field { - field_compiler.compile_stmt(node, true)?; + field_compiler.compile_expr(node, true)?; } else { field_compiler.emit_opcode(Opcode::PushUndefined); } @@ -2996,7 +2996,7 @@ impl<'b> ByteCompiler<'b> { }; field_compiler.context.push_compile_time_environment(true); if let Some(node) = field { - field_compiler.compile_stmt(node, true)?; + field_compiler.compile_expr(node, true)?; } else { field_compiler.emit_opcode(Opcode::PushUndefined); } @@ -3020,7 +3020,7 @@ impl<'b> ByteCompiler<'b> { match name { PropertyName::Literal(name) => { if let Some(node) = field { - self.compile_stmt(node, true)?; + self.compile_expr(node, true)?; } else { self.emit_opcode(Opcode::PushUndefined); } @@ -3029,10 +3029,10 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); if let Some(node) = field { - self.compile_stmt(node, true)?; + self.compile_expr(node, true)?; } else { self.emit_opcode(Opcode::PushUndefined); } @@ -3043,7 +3043,7 @@ impl<'b> ByteCompiler<'b> { ClassElement::PrivateStaticFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); if let Some(node) = field { - self.compile_stmt(node, true)?; + self.compile_expr(node, true)?; } else { self.emit_opcode(Opcode::PushUndefined); } @@ -3054,8 +3054,8 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); let mut compiler = ByteCompiler::new(Sym::EMPTY_STRING, true, self.context); compiler.context.push_compile_time_environment(true); - compiler.create_declarations(statement_list.items())?; - compiler.compile_statement_list(statement_list.items(), false)?; + compiler.create_decls(statement_list.statements())?; + compiler.compile_statement_list(statement_list.statements(), false)?; let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); compiler @@ -3076,32 +3076,32 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); match method_definition { MethodDefinition::Get(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; let index = self.get_or_insert_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } @@ -3120,85 +3120,85 @@ impl<'b> ByteCompiler<'b> { match method_definition { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassGetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassGetterByValue); } }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassSetterByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassSetterByValue); } }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); let index = self.get_or_insert_name(*name); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { - self.compile_stmt(name_node, true)?; + self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(&expr.clone().into(), true)?; + self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, @@ -3218,16 +3218,11 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Pop); if !expression { - self.emit_binding(BindingOpcode::InitVar, class.name()); + self.emit_binding( + BindingOpcode::InitVar, + class.name().expect("class statements must have a name"), + ); } Ok(()) } } - -/// `FunctionKind` describes how a function has been defined in the source code. -#[derive(Debug, Clone, Copy, PartialEq)] -pub(crate) enum FunctionKind { - Declaration, - Expression, - Arrow, -} diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 505725c11b8..5cf9ccd3933 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -19,7 +19,7 @@ use crate::{ object::{FunctionBuilder, GlobalPropertyMap, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - syntax::{ast::node::StatementList, parser::ParseError, Parser}, + syntax::{ast::statement::StatementList, parser::ParseError, Parser}, vm::{CallFrame, CodeBlock, FinallyReturn, GeneratorResumeKind, Vm}, JsResult, JsString, JsValue, }; @@ -681,8 +681,8 @@ impl Context { pub fn compile(&mut self, statement_list: &StatementList) -> JsResult> { let _timer = Profiler::global().start_event("Compilation", "Main"); let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), self); - compiler.create_declarations(statement_list.items())?; - compiler.compile_statement_list(statement_list.items(), true)?; + compiler.create_decls(statement_list.statements())?; + compiler.compile_statement_list(statement_list.statements(), true)?; Ok(Gc::new(compiler.finish())) } @@ -696,7 +696,7 @@ impl Context { let _timer = Profiler::global().start_event("Compilation", "Main"); let mut compiler = ByteCompiler::new(Sym::MAIN, statement_list.strict(), self); compiler.compile_statement_list_with_new_declarative( - statement_list.items(), + statement_list.statements(), true, strict || statement_list.strict(), )?; diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_engine/src/syntax/ast/expression/access.rs new file mode 100644 index 00000000000..b69d22564ea --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/access.rs @@ -0,0 +1,187 @@ +use crate::syntax::ast::expression::Expression; +use boa_interner::{Interner, Sym, ToInternedString}; + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyAccessField { + Const(Sym), + Expr(Box), +} + +impl From for PropertyAccessField { + fn from(id: Sym) -> Self { + Self::Const(id) + } +} + +impl From for PropertyAccessField { + fn from(expr: Expression) -> Self { + Self::Expr(Box::new(expr)) + } +} + +/// This property accessor provides access to an object's properties by using the +/// [bracket notation][mdn]. +/// +/// In the `object[property_name]` syntax, the `property_name` is just a string or +/// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a +/// space). +/// +/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup +/// table). The keys in this array are the names of the object's properties. +/// +/// It's typical when speaking of an object's properties to make a distinction between +/// properties and methods. However, the property/method distinction is little more than a +/// convention. A method is simply a property that can be called (for example, if it has a +/// reference to a Function instance as its value). +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-property-accessors +/// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct PropertyAccess { + target: Box, + field: PropertyAccessField, +} + +impl PropertyAccess { + pub fn target(&self) -> &Expression { + &self.target + } + + pub fn field(&self) -> &PropertyAccessField { + &self.field + } + + /// Creates a `PropertyAccess` AST Expression. + pub fn new(target: Expression, field: F) -> Self + where + F: Into, + { + Self { + target: target.into(), + field: field.into(), + } + } +} + +impl ToInternedString for PropertyAccess { + fn to_interned_string(&self, interner: &Interner) -> String { + let target = self.target.to_interned_string(interner); + match self.field { + PropertyAccessField::Const(sym) => format!("{target}.{}", interner.resolve_expect(sym)), + PropertyAccessField::Expr(ref expr) => { + format!("{target}[{}]", expr.to_interned_string(interner)) + } + } + } +} + +impl From for Expression { + fn from(access: PropertyAccess) -> Self { + Self::PropertyAccess(access) + } +} + +/// This property accessor provides access to an class object's private fields. +/// +/// This expression can be described as ` MemberExpression.PrivateIdentifier` +/// Example: `this.#a` +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct PrivatePropertyAccess { + target: Box, + field: Sym, +} + +impl PrivatePropertyAccess { + /// Creates a `GetPrivateField` AST Expression. + pub fn new(value: Expression, field: Sym) -> Self { + Self { + target: value.into(), + field, + } + } + + /// Gets the original object from where to get the field from. + pub fn target(&self) -> &Expression { + &self.target + } + + /// Gets the name of the field to retrieve. + pub fn field(&self) -> Sym { + self.field + } +} + +impl ToInternedString for PrivatePropertyAccess { + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{}.#{}", + self.target.to_interned_string(interner), + interner.resolve_expect(self.field) + ) + } +} + +impl From for Expression { + fn from(access: PrivatePropertyAccess) -> Self { + Self::PrivatePropertyAccess(access) + } +} + +/// The `super` keyword is used to access fields on an object's parent. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct SuperPropertyAccess { + field: PropertyAccessField, +} + +impl SuperPropertyAccess { + pub(in crate::syntax) fn new(field: PropertyAccessField) -> Self { + Self { field } + } + + /// Gets the name of the field to retrieve. + pub fn field(&self) -> &PropertyAccessField { + &self.field + } +} + +impl ToInternedString for SuperPropertyAccess { + fn to_interned_string(&self, interner: &Interner) -> String { + match &self.field { + PropertyAccessField::Const(field) => { + format!("super.{}", interner.resolve_expect(*field)) + } + PropertyAccessField::Expr(field) => { + format!("super[{}]", field.to_interned_string(interner)) + } + } + } +} + +impl From for Expression { + fn from(access: SuperPropertyAccess) -> Self { + Self::SuperPropertyAccess(access) + } +} diff --git a/boa_engine/src/syntax/ast/node/await_expr/mod.rs b/boa_engine/src/syntax/ast/expression/await.rs similarity index 50% rename from boa_engine/src/syntax/ast/node/await_expr/mod.rs rename to boa_engine/src/syntax/ast/expression/await.rs index 6ae00f9e0d8..42b50a7086d 100644 --- a/boa_engine/src/syntax/ast/node/await_expr/mod.rs +++ b/boa_engine/src/syntax/ast/expression/await.rs @@ -1,14 +1,8 @@ -//! Await expression node. +//! Await expression Expression. -use super::Node; +use super::Expression; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - /// An await expression is used within an async function to pause execution and wait for a /// promise to resolve. /// @@ -18,36 +12,51 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct AwaitExpr { - expr: Box, +pub struct Await { + expr: Box, } -impl AwaitExpr { +impl Await { /// Return the expression that should be awaited. - pub(crate) fn expr(&self) -> &Node { + pub(crate) fn expr(&self) -> &Expression { &self.expr } } -impl From for AwaitExpr +impl From for Await where - T: Into>, + T: Into>, { fn from(e: T) -> Self { Self { expr: e.into() } } } -impl ToInternedString for AwaitExpr { +impl ToInternedString for Await { fn to_interned_string(&self, interner: &Interner) -> String { format!("await {}", self.expr.to_indented_string(interner, 0)) } } -impl From for Node { - fn from(awaitexpr: AwaitExpr) -> Self { - Self::AwaitExpr(awaitexpr) +impl From for Expression { + fn from(awaitexpr: Await) -> Self { + Self::Await(awaitexpr) + } +} + +#[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_engine/src/syntax/ast/expression/call.rs new file mode 100644 index 00000000000..ebef884e93e --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/call.rs @@ -0,0 +1,116 @@ +use crate::syntax::ast::join_nodes; +use boa_interner::{Interner, ToInternedString}; + +use super::Expression; + +/// Calling the function actually performs the specified actions with the indicated parameters. +/// +/// Defining a function does not execute it. Defining it simply names the function and +/// specifies what to do when the function is called. Functions must be in scope when they are +/// called, but the function declaration can be hoisted. The scope of a function is the +/// function in which it is declared (or the entire program, if it is declared at the top +/// level). +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct Call { + target: Box, + args: Box<[Expression]>, +} + +impl Call { + /// Creates a new `Call` AST Expression. + pub fn new(target: Expression, args: Box<[Expression]>) -> Self { + Self { + target: target.into(), + args, + } + } + + /// Gets the name of the function call. + pub fn expr(&self) -> &Expression { + &self.target + } + + /// Retrieves the arguments passed to the function. + pub fn args(&self) -> &[Expression] { + &self.args + } +} + +impl ToInternedString for Call { + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{}({})", + self.target.to_interned_string(interner), + join_nodes(interner, &self.args) + ) + } +} + +impl From for Expression { + fn from(call: Call) -> Self { + Self::Call(call) + } +} + +/// The `super` keyword is used to access and call functions on an object's parent. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct SuperCall { + args: Box<[Expression]>, +} + +impl SuperCall { + /// Creates a new `SuperCall` AST node. + pub(crate) fn new(args: A) -> Self + where + A: Into>, + { + Self { args: args.into() } + } + + /// Retrieves the arguments of the super call. + pub(crate) fn args(&self) -> &[Expression] { + &self.args + } +} + +impl ToInternedString for SuperCall { + fn to_interned_string(&self, interner: &Interner) -> String { + format!("super({})", join_nodes(interner, &self.args)) + } +} + +impl From for Expression { + fn from(call: SuperCall) -> Self { + Self::SuperCall(call) + } +} + +#[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/node/identifier/mod.rs b/boa_engine/src/syntax/ast/expression/identifier.rs similarity index 87% rename from boa_engine/src/syntax/ast/node/identifier/mod.rs rename to boa_engine/src/syntax/ast/expression/identifier.rs index 62d7fbef80e..26db2cb4522 100644 --- a/boa_engine/src/syntax/ast/node/identifier/mod.rs +++ b/boa_engine/src/syntax/ast/expression/identifier.rs @@ -1,16 +1,13 @@ -//! Local identifier node. +//! Local identifier Expression. use crate::{ string::ToStringEscaped, - syntax::{ - ast::{node::Node, Position}, - parser::ParseError, - }, + syntax::{ast::Position, parser::ParseError}, }; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::Expression; + /// An `identifier` is a sequence of characters in the code that identifies a variable, /// function, or property. /// @@ -27,7 +24,7 @@ use serde::{Deserialize, Serialize}; /// /// [spec]: https://tc39.es/ecma262/#prod-Identifier /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Identifier { @@ -35,7 +32,7 @@ pub struct Identifier { } impl Identifier { - /// Creates a new identifier AST node. + /// Creates a new identifier AST Expression. pub fn new(ident: Sym) -> Self { Self { ident } } @@ -80,7 +77,7 @@ impl From for Identifier { } } -impl From for Node { +impl From for Expression { fn from(local: Identifier) -> Self { Self::Identifier(local) } diff --git a/boa_engine/src/syntax/ast/node/array/mod.rs b/boa_engine/src/syntax/ast/expression/literal/array.rs similarity index 55% rename from boa_engine/src/syntax/ast/node/array/mod.rs rename to boa_engine/src/syntax/ast/expression/literal/array.rs index 0d008045cec..29f03f4e8d1 100644 --- a/boa_engine/src/syntax/ast/node/array/mod.rs +++ b/boa_engine/src/syntax/ast/expression/literal/array.rs @@ -1,14 +1,8 @@ -//! Array declaration node. +//! Array declaration Expression. -use super::{join_nodes, Node}; +use crate::syntax::ast::expression::Expression; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - /// An array is an ordered collection of data (either primitive or object depending upon the /// language). /// @@ -25,18 +19,18 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct ArrayDecl { - arr: Box<[Node]>, +pub struct ArrayLiteral { + arr: Box<[Option]>, has_trailing_comma_spread: bool, } -impl ArrayDecl { - /// Crate a new array declaration. +impl ArrayLiteral { + /// Crate a new array literal. pub(crate) fn new(array: A, has_trailing_comma_spread: bool) -> Self where - A: Into>, + A: Into]>>, { Self { arr: array.into(), @@ -51,15 +45,15 @@ impl ArrayDecl { } } -impl AsRef<[Node]> for ArrayDecl { - fn as_ref(&self) -> &[Node] { +impl AsRef<[Option]> for ArrayLiteral { + fn as_ref(&self) -> &[Option] { &self.arr } } -impl From for ArrayDecl +impl From for ArrayLiteral where - T: Into>, + T: Into]>>, { fn from(decl: T) -> Self { Self { @@ -69,14 +63,40 @@ where } } -impl ToInternedString for ArrayDecl { +impl ToInternedString for ArrayLiteral { fn to_interned_string(&self, interner: &Interner) -> String { - format!("[{}]", join_nodes(interner, &self.arr)) + 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 Node { - fn from(arr: ArrayDecl) -> Self { - Self::ArrayDecl(arr) +impl From for Expression { + fn from(arr: ArrayLiteral) -> Self { + Self::ArrayLiteral(arr) + } +} + +#[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/constant.rs b/boa_engine/src/syntax/ast/expression/literal/mod.rs similarity index 89% rename from boa_engine/src/syntax/ast/constant.rs rename to boa_engine/src/syntax/ast/expression/literal/mod.rs index 1f1ced053ac..3a5c8125b3c 100644 --- a/boa_engine/src/syntax/ast/constant.rs +++ b/boa_engine/src/syntax/ast/expression/literal/mod.rs @@ -1,4 +1,4 @@ -//! This module implements the `Const` structure, which represents the primitive values in JavaScript. +//! This module contains all literal expressions, which represents the primitive values in JavaScript. //! //! More information: //! - [ECMAScript reference][spec] @@ -7,10 +7,18 @@ //! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals +mod array; +mod object; +mod template; + +pub use array::ArrayLiteral; +pub use object::ObjectLiteral; +pub use template::{TemplateElement, TemplateLiteral}; + use boa_interner::{Interner, Sym, ToInternedString}; use num_bigint::BigInt; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; + +use super::Expression; /// Literals represent values in JavaScript. /// @@ -22,9 +30,9 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub enum Const { +pub enum Literal { /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. /// /// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks). @@ -111,43 +119,49 @@ pub enum Const { Undefined, } -impl From for Const { +impl From for Literal { fn from(string: Sym) -> Self { Self::String(string) } } -impl From for Const { +impl From for Literal { fn from(num: f64) -> Self { Self::Num(num) } } -impl From for Const { +impl From for Literal { fn from(i: i32) -> Self { Self::Int(i) } } -impl From for Const { +impl From for Literal { fn from(i: BigInt) -> Self { Self::BigInt(Box::new(i)) } } -impl From> for Const { +impl From> for Literal { fn from(i: Box) -> Self { Self::BigInt(i) } } -impl From for Const { +impl From for Literal { fn from(b: bool) -> Self { Self::Bool(b) } } -impl ToInternedString for Const { +impl From for Expression { + fn from(lit: Literal) -> Self { + Expression::Literal(lit) + } +} + +impl ToInternedString for Literal { fn to_interned_string(&self, interner: &Interner) -> String { match *self { Self::String(st) => { diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs new file mode 100644 index 00000000000..d071a782ac5 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs @@ -0,0 +1,142 @@ +//! Object Expression. + +#[cfg(test)] +mod tests; + +use crate::syntax::ast::expression::Expression; + +use crate::syntax::ast::property::MethodDefinition; +use crate::syntax::ast::property::PropertyDefinition; +use crate::syntax::ast::{block_to_string, join_nodes}; +use boa_interner::{Interner, ToInternedString}; + +/// 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 { + pub fn properties(&self) -> &[PropertyDefinition] { + &self.properties + } + + /// Implements the display formatting with indentation. + pub(crate) 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)) + } + 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(method, key) => { + 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), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + }); + } + buf.push_str(&format!("{}}}", " ".repeat(indent_n))); + + buf + } +} + +impl ToInternedString for ObjectLiteral { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for ObjectLiteral +where + T: Into>, +{ + fn from(props: T) -> Self { + Self { + properties: props.into(), + } + } +} + +impl From for Expression { + fn from(obj: ObjectLiteral) -> Self { + Self::ObjectLiteral(obj) + } +} diff --git a/boa_engine/src/syntax/ast/node/object/tests.rs b/boa_engine/src/syntax/ast/expression/literal/object/tests.rs similarity index 98% rename from boa_engine/src/syntax/ast/node/object/tests.rs rename to boa_engine/src/syntax/ast/expression/literal/object/tests.rs index 4d49abac81f..3f078f5645f 100644 --- a/boa_engine/src/syntax/ast/node/object/tests.rs +++ b/boa_engine/src/syntax/ast/expression/literal/object/tests.rs @@ -75,7 +75,7 @@ fn spread_null_and_undefined_ignored() { #[test] fn fmt() { - super::super::test_formatting( + crate::syntax::ast::test_formatting( r#" let other = { c: 10, diff --git a/boa_engine/src/syntax/ast/expression/literal/template.rs b/boa_engine/src/syntax/ast/expression/literal/template.rs new file mode 100644 index 00000000000..b4b79ff7444 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/literal/template.rs @@ -0,0 +1,98 @@ +//! Template literal Expression. + +use std::borrow::Cow; + +use boa_interner::{Interner, Sym, ToInternedString}; + +use crate::{string::ToStringEscaped, syntax::ast::expression::Expression}; + +/// Template literals are string literals allowing embedded expressions. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct TemplateLiteral { + elements: Box<[TemplateElement]>, +} + +impl From for Expression { + fn from(tem: TemplateLiteral) -> Self { + Self::TemplateLiteral(tem) + } +} + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum TemplateElement { + String(Sym), + Expr(Expression), +} + +impl TemplateLiteral { + pub fn new(elements: Box<[TemplateElement]>) -> Self { + Self { elements } + } + + pub(crate) fn elements(&self) -> &[TemplateElement] { + &self.elements + } +} + +impl ToInternedString for TemplateLiteral { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "`".to_owned(); + + for elt in self.elements.iter() { + match elt { + TemplateElement::String(s) => buf.push_str(&interner.resolve_expect(*s).join( + Cow::Borrowed, + |utf16| Cow::Owned(utf16.to_string_escaped()), + true, + )), + TemplateElement::Expr(n) => { + buf.push_str(&format!("${{{}}}", n.to_interned_string(interner))); + } + } + } + buf.push('`'); + + buf + } +} + +#[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_engine/src/syntax/ast/expression/mod.rs new file mode 100644 index 00000000000..579ab0359ad --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/mod.rs @@ -0,0 +1,583 @@ +use boa_interner::{Interner, Sym, ToInternedString}; + +use self::{ + access::{PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SuperPropertyAccess}, + literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral}, + operator::{assign::AssignTarget, conditional::Conditional, Assign, Binary, Unary}, +}; + +use super::{ + function::FormalParameterList, + function::{ + ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator, + }, + property::{MethodDefinition, PropertyDefinition}, + ContainsSymbol, Statement, +}; + +mod r#await; +mod call; +mod identifier; +mod new; +mod spread; +mod tagged_template; +mod r#yield; + +pub use call::{Call, SuperCall}; +pub use identifier::Identifier; +pub use new::New; +pub use r#await::Await; +pub use r#yield::Yield; +pub use spread::Spread; +pub use tagged_template::TaggedTemplate; + +pub mod access; +pub mod literal; +pub mod operator; + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq)] +pub enum Expression { + /// The JavaScript `this` keyword refers to the object it belongs to. + /// + /// A property of an execution context (global, function or eval) that, + /// in non–strict mode, is always a reference to an object and in strict + /// mode can be any value. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-this-keyword + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this + This, + + /// See [`Identifier`]. + Identifier(Identifier), + + /// See [`Literal`]. + Literal(Literal), + + /// See [`ArrayLiteral`]. + ArrayLiteral(ArrayLiteral), + + /// See [`ObjectLiteral`]. + ObjectLiteral(ObjectLiteral), + + /// See [`Spread`], + Spread(Spread), + + /// See [`Function`]. + Function(Function), + + /// See [`ArrowFunction`]. + ArrowFunction(ArrowFunction), + + /// See [`Generator`]. + Generator(Generator), + + /// See [`AsyncFunction`]. + AsyncFunction(AsyncFunction), + + /// See [`AsyncGenerator`]. + AsyncGenerator(AsyncGenerator), + + /// See [`Class`]. + Class(Box), + + // TODO: Extract regexp literal Expression + // RegExpLiteral, + /// See [`TemplateLiteral`]. + TemplateLiteral(TemplateLiteral), + + /// See [`PropertyAccess`]. + PropertyAccess(PropertyAccess), + + /// See [`SuperPropertyAccess`] + SuperPropertyAccess(SuperPropertyAccess), + + /// See [`PrivatePropertyAccess]. + PrivatePropertyAccess(PrivatePropertyAccess), + + /// See [`New`]. + New(New), + + /// See [`Call`]. + Call(Call), + + /// See [`SuperCall`] + SuperCall(SuperCall), + + // TODO: Optional chains + + // TODO: Import calls + /// See [`TaggedTemplate`]. + TaggedTemplate(TaggedTemplate), + + /// The `new.target` pseudo-property expression. + NewTarget, + + // TODO: import.meta + /// See [`Assign`]. + Assign(Assign), + + /// See [`Unary`]. + Unary(Unary), + + /// See [`Binary`]. + Binary(Binary), + + /// See [`Conditional`]. + Conditional(Conditional), + + /// See [`Await`]. + Await(Await), + + /// See [`Yield`]. + Yield(Yield), + + /// A FormalParameterList. + /// + /// This is only used in the parser itself. + /// It is not a valid AST node. + #[doc(hidden)] + FormalParameterList(FormalParameterList), +} + +impl Expression { + /// Creates a string of the value of the expression with the given indentation. + pub fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + self.to_no_indent_string(interner, indentation) + } + + /// Implements the display formatting with indentation. + /// + /// 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(crate) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { + match self { + Self::This => "this".to_owned(), + Self::Identifier(id) => id.to_interned_string(interner), + Self::Literal(lit) => lit.to_interned_string(interner), + Self::ArrayLiteral(arr) => arr.to_interned_string(interner), + Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation), + Self::Spread(sp) => sp.to_interned_string(interner), + Self::Function(f) => f.to_indented_string(interner, indentation), + Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation), + Self::Class(cl) => cl.to_indented_string(interner, indentation), + Self::Generator(gen) => gen.to_indented_string(interner, indentation), + Self::AsyncFunction(asf) => asf.to_indented_string(interner, indentation), + Self::AsyncGenerator(asgen) => asgen.to_indented_string(interner, indentation), + Self::TemplateLiteral(tem) => tem.to_interned_string(interner), + Self::PropertyAccess(prop) => prop.to_interned_string(interner), + Self::SuperPropertyAccess(supp) => supp.to_interned_string(interner), + Self::PrivatePropertyAccess(private) => private.to_interned_string(interner), + Self::New(new) => new.to_interned_string(interner), + Self::Call(call) => call.to_interned_string(interner), + Self::SuperCall(supc) => supc.to_interned_string(interner), + Self::NewTarget => "new.target".to_owned(), + Self::TaggedTemplate(tag) => tag.to_interned_string(interner), + Self::Assign(assign) => assign.to_interned_string(interner), + Self::Unary(unary) => unary.to_interned_string(interner), + Self::Binary(bin) => bin.to_interned_string(interner), + Self::Conditional(cond) => cond.to_interned_string(interner), + Self::Await(aw) => aw.to_interned_string(interner), + Self::Yield(yi) => yi.to_interned_string(interner), + Self::FormalParameterList(_) => unreachable!(), + } + } + + /// Returns true if the expression contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + // TODO: replace with a visitor + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Expression::Identifier(ident) if ident.sym() == Sym::ARGUMENTS => return true, + Expression::ArrayLiteral(array) => { + for expr in array.as_ref() { + if matches!(expr, Some(expr) if expr.contains_arguments()) { + return true; + } + } + } + Expression::ObjectLiteral(object) => { + for property in object.properties() { + match property { + PropertyDefinition::IdentifierReference(ident) => { + if *ident == Sym::ARGUMENTS { + return true; + } + } + PropertyDefinition::Property(_, node) + | PropertyDefinition::SpreadObject(node) => { + if node.contains_arguments() { + return true; + } + } + PropertyDefinition::MethodDefinition(method, _) => match method { + MethodDefinition::Get(function) + | MethodDefinition::Set(function) + | MethodDefinition::Ordinary(function) => { + if let Some(Sym::ARGUMENTS) = function.name() { + return true; + } + } + MethodDefinition::Generator(generator) => { + if let Some(Sym::ARGUMENTS) = generator.name() { + return true; + } + } + MethodDefinition::AsyncGenerator(async_generator) => { + if let Some(Sym::ARGUMENTS) = async_generator.name() { + return true; + } + } + MethodDefinition::Async(function) => { + if let Some(Sym::ARGUMENTS) = function.name() { + return true; + } + } + }, + PropertyDefinition::CoverInitializedName(_, _) => {} + } + } + } + Expression::Spread(spread) => { + if spread.val().contains_arguments() { + return true; + } + } + Expression::Assign(assign) => { + if assign.rhs().contains_arguments() { + return true; + } + } + Expression::Await(r#await) => { + if r#await.expr().contains_arguments() { + return true; + } + } + Expression::Binary(bin_op) => { + if bin_op.lhs().contains_arguments() || bin_op.rhs().contains_arguments() { + return true; + } + } + Expression::Call(call) => { + if call.expr().contains_arguments() { + return true; + } + for node in call.args() { + if node.contains_arguments() { + return true; + } + } + } + Expression::Conditional(conditional) => { + if conditional.cond().contains_arguments() { + return true; + } + if conditional.if_true().contains_arguments() { + return true; + } + if conditional.if_false().contains_arguments() { + return true; + } + } + Expression::PropertyAccess(access) => { + if access.target().contains_arguments() { + return true; + } + if let PropertyAccessField::Expr(expr) = access.field() { + if expr.contains_arguments() { + return true; + } + } + } + Expression::PrivatePropertyAccess(access) => { + if access.target().contains_arguments() { + return true; + } + } + Expression::New(new) => { + if new.expr().contains_arguments() { + return true; + } + for node in new.args() { + if node.contains_arguments() { + return true; + } + } + } + Expression::TaggedTemplate(tagged_template) => { + if tagged_template.tag().contains_arguments() { + return true; + } + for node in tagged_template.exprs() { + if node.contains_arguments() { + return true; + } + } + } + Expression::TemplateLiteral(template_lit) => { + for element in template_lit.elements() { + if let TemplateElement::Expr(node) = element { + if node.contains_arguments() { + return false; + } + } + } + } + Expression::Unary(unary_op) => { + if unary_op.target().contains_arguments() { + return true; + } + } + Expression::Yield(r#yield) => { + if let Some(node) = r#yield.expr() { + if node.contains_arguments() { + return true; + } + } + } + Expression::Class(class) => { + if let Some(node) = class.super_ref() { + if node.contains_arguments() { + return true; + } + for element in class.elements() { + match element { + ClassElement::MethodDefinition(_, method) + | ClassElement::StaticMethodDefinition(_, method) => match method { + MethodDefinition::Get(function) + | MethodDefinition::Set(function) + | MethodDefinition::Ordinary(function) => { + if let Some(Sym::ARGUMENTS) = function.name() { + return true; + } + } + MethodDefinition::Generator(generator) => { + if let Some(Sym::ARGUMENTS) = generator.name() { + return true; + } + } + MethodDefinition::AsyncGenerator(async_generator) => { + if let Some(Sym::ARGUMENTS) = async_generator.name() { + return true; + } + } + MethodDefinition::Async(function) => { + if let Some(Sym::ARGUMENTS) = function.name() { + return true; + } + } + }, + ClassElement::FieldDefinition(_, node) + | ClassElement::StaticFieldDefinition(_, node) + | ClassElement::PrivateFieldDefinition(_, node) + | ClassElement::PrivateStaticFieldDefinition(_, node) => { + if let Some(node) = node { + if node.contains_arguments() { + return true; + } + } + } + ClassElement::StaticBlock(statement_list) => { + for node in statement_list.statements() { + if node.contains_arguments() { + return true; + } + } + } + _ => {} + } + } + } + } + _ => {} + } + false + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + // TODO: replace with a visitor + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Expression::ArrayLiteral(array) => { + for expr in array.as_ref().iter().flatten() { + if expr.contains(symbol) { + return true; + } + } + } + Expression::Assign(assign) => { + match assign.lhs() { + AssignTarget::Property(field) => { + if field.target().contains(symbol) { + return true; + } + match field.field() { + PropertyAccessField::Expr(expr) if expr.contains(symbol) => { + return true + } + _ => {} + } + } + AssignTarget::PrivateProperty(access) => { + if access.target().contains(symbol) { + return true; + } + } + AssignTarget::SuperProperty(_) => { + if symbol == ContainsSymbol::SuperProperty { + return true; + } + } + AssignTarget::Pattern(pattern) => { + if pattern.contains(symbol) { + return true; + } + } + AssignTarget::Identifier(_) => {} + } + if assign.rhs().contains(symbol) { + return true; + } + } + Expression::Await(_) if symbol == ContainsSymbol::AwaitExpression => return true, + Expression::Await(expr) => { + if expr.expr().contains(symbol) { + return true; + } + } + Expression::Binary(bin_op) => { + if bin_op.lhs().contains(symbol) || bin_op.rhs().contains(symbol) { + return true; + } + } + Expression::Call(call) => { + if call.expr().contains(symbol) { + return true; + } + for node in call.args() { + if node.contains(symbol) { + return true; + } + } + } + Expression::New(new) => { + if new.call().expr().contains(symbol) { + return true; + } + for node in new.call().args() { + if node.contains(symbol) { + return true; + } + } + } + Expression::Spread(spread) => { + if spread.val().contains(symbol) { + return true; + } + } + Expression::TaggedTemplate(template) => { + if template.tag().contains(symbol) { + return true; + } + for node in template.exprs() { + if node.contains(symbol) { + return true; + } + } + } + Expression::TemplateLiteral(template) => { + for element in template.elements() { + if let TemplateElement::Expr(node) = element { + if node.contains(symbol) { + return true; + } + } + } + } + Expression::SuperCall(_) if symbol == ContainsSymbol::SuperCall => return true, + Expression::SuperPropertyAccess(_) if symbol == ContainsSymbol::SuperProperty => { + return true + } + Expression::ObjectLiteral(object) => { + for property in object.properties() { + match property { + PropertyDefinition::Property(name, init) => { + if let Some(node) = name.computed() { + if node.contains(symbol) { + return true; + } + } + if init.contains(symbol) { + return true; + } + } + PropertyDefinition::SpreadObject(spread) => { + if spread.contains(symbol) { + return true; + } + } + PropertyDefinition::MethodDefinition(_, name) => { + if let Some(node) = name.computed() { + if node.contains(symbol) { + return true; + } + } + } + PropertyDefinition::IdentifierReference(_) + | PropertyDefinition::CoverInitializedName(_, _) => {} + } + } + } + Expression::Class(class) => { + if let Some(node) = class.super_ref() { + if node.contains(symbol) { + return true; + } + } + for element in class.elements() { + match element { + ClassElement::MethodDefinition(name, _) + | ClassElement::StaticMethodDefinition(name, _) + | ClassElement::FieldDefinition(name, _) + | ClassElement::StaticFieldDefinition(name, _) => { + if let Some(node) = name.computed() { + if node.contains(symbol) { + return true; + } + } + } + _ => {} + } + } + } + Expression::Yield(_) if symbol == ContainsSymbol::YieldExpression => return true, + _ => {} + } + false + } +} + +impl From for Statement { + fn from(expr: Expression) -> Self { + Statement::Expression(expr) + } +} + +impl ToInternedString for Expression { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} diff --git a/boa_engine/src/syntax/ast/node/new/mod.rs b/boa_engine/src/syntax/ast/expression/new.rs similarity index 76% rename from boa_engine/src/syntax/ast/node/new/mod.rs rename to boa_engine/src/syntax/ast/expression/new.rs index 024959779b3..86e576067f1 100644 --- a/boa_engine/src/syntax/ast/node/new/mod.rs +++ b/boa_engine/src/syntax/ast/expression/new.rs @@ -1,11 +1,7 @@ -use crate::syntax::ast::node::{Call, Node}; +use crate::syntax::ast::expression::Call; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; +use super::Expression; /// The `new` operator lets developers create an instance of a user-defined object type or of /// one of the built-in object types that has a constructor function. @@ -22,7 +18,7 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct New { call: Call, @@ -30,12 +26,12 @@ pub struct New { impl New { /// Gets the name of the function call. - pub fn expr(&self) -> &Node { + pub fn expr(&self) -> &Expression { self.call.expr() } /// Retrieves the arguments passed to the function. - pub fn args(&self) -> &[Node] { + pub fn args(&self) -> &[Expression] { self.call.args() } @@ -57,8 +53,21 @@ impl ToInternedString for New { } } -impl From for Node { +impl From for Expression { fn from(new: New) -> Self { Self::New(new) } } + +#[cfg(test)] +mod tests { + #[test] + fn fmt() { + crate::syntax::ast::test_formatting( + r#" + function MyClass() {} + let inst = new MyClass(); + "#, + ); + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs new file mode 100644 index 00000000000..0c2c7de22d8 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs @@ -0,0 +1,414 @@ +use boa_interner::{Interner, Sym, ToInternedString}; + +use crate::syntax::{ + ast::{ + expression::{ + access::{PrivatePropertyAccess, PropertyAccess, SuperPropertyAccess}, + identifier::Identifier, + literal::{ArrayLiteral, ObjectLiteral}, + Expression, + }, + pattern::{ + Pattern, PatternArray, PatternArrayElement, PatternObject, PatternObjectElement, + }, + property::{PropertyDefinition, PropertyName}, + }, + parser::RESERVED_IDENTIFIERS_STRICT, +}; + +pub mod op; + +/// An assignment operator assigns a value to its left operand based on the value of its right +/// operand. +/// +/// Assignment operator (`=`), assigns the value of its right operand to its left operand. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct Assign { + op: op::AssignOp, + lhs: Box, + rhs: Box, +} + +impl Assign { + /// Creates an `Assign` AST Expression. + pub(in crate::syntax) fn new(op: op::AssignOp, lhs: AssignTarget, rhs: Expression) -> Self { + Self { + op, + lhs: Box::new(lhs), + rhs: Box::new(rhs), + } + } + + /// Gets the operator of the assignment operation. + pub fn op(&self) -> op::AssignOp { + self.op + } + + /// Gets the left hand side of the assignment operation. + pub fn lhs(&self) -> &AssignTarget { + &self.lhs + } + + /// Gets the right hand side of the assignment operation. + pub fn rhs(&self) -> &Expression { + &self.rhs + } +} + +impl ToInternedString for Assign { + 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 { + fn from(op: Assign) -> Self { + Self::Assign(op) + } +} + +/// This type represents all valid left-had-side expressions of an assignment operator. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum AssignTarget { + Identifier(Identifier), + Property(PropertyAccess), + PrivateProperty(PrivatePropertyAccess), + SuperProperty(SuperPropertyAccess), + Pattern(Pattern), +} + +impl AssignTarget { + /// Converts the left-hand-side Expression of an assignment expression into it's 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::Property(access.clone())), + Expression::PrivatePropertyAccess(access) => { + Some(Self::PrivateProperty(access.clone())) + } + Expression::SuperPropertyAccess(access) => Some(Self::SuperProperty(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 { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + AssignTarget::Identifier(id) => id.to_interned_string(interner), + AssignTarget::Property(access) => access.to_interned_string(interner), + AssignTarget::PrivateProperty(access) => access.to_interned_string(interner), + AssignTarget::SuperProperty(access) => access.to_interned_string(interner), + AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner), + } + } +} + +impl From for AssignTarget { + 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) { + return None; + } + + excluded_keys.push(*ident); + bindings.push(PatternObjectElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*ident), + default_init: None, + }); + } + PropertyDefinition::Property(name, expr) => match (name, expr) { + (PropertyName::Literal(name), Expression::Identifier(ident)) + if *name == ident.sym() => + { + if strict && *name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) { + return None; + } + + excluded_keys.push(*name); + bindings.push(PatternObjectElement::SingleName { + ident: *name, + name: PropertyName::Literal(*name), + default_init: None, + }); + } + (PropertyName::Literal(name), Expression::Identifier(ident)) => { + bindings.push(PatternObjectElement::SingleName { + ident: ident.sym(), + 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(PatternObjectElement::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(PatternObjectElement::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.sym() { + if strict && name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { + return None; + } + excluded_keys.push(name); + bindings.push(PatternObjectElement::SingleName { + ident: name, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } else { + bindings.push(PatternObjectElement::SingleName { + ident: ident.sym(), + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } + } else { + return None; + } + } + AssignTarget::Pattern(pattern) => { + bindings.push(PatternObjectElement::Pattern { + name: name.clone(), + pattern: pattern.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Property(field) => { + bindings.push(PatternObjectElement::AssignmentPropertyAccess { + name: name.clone(), + access: field.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::SuperProperty(_) | AssignTarget::PrivateProperty(_) => { + return None + } + }, + (_, Expression::PropertyAccess(field)) => { + bindings.push(PatternObjectElement::AssignmentPropertyAccess { + name: name.clone(), + access: field.clone(), + default_init: None, + }); + } + (PropertyName::Computed(name), Expression::Identifier(ident)) => { + bindings.push(PatternObjectElement::SingleName { + ident: ident.sym(), + name: PropertyName::Computed(name.clone()), + default_init: None, + }); + } + _ => return None, + }, + PropertyDefinition::SpreadObject(spread) => { + match spread { + Expression::Identifier(ident) => { + bindings.push(PatternObjectElement::RestProperty { + ident: ident.sym(), + excluded_keys: excluded_keys.clone(), + }); + } + Expression::PropertyAccess(access) => { + bindings.push(PatternObjectElement::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 && (*ident == Sym::EVAL || *ident == Sym::ARGUMENTS) { + return None; + } + + bindings.push(PatternObjectElement::SingleName { + ident: *ident, + name: PropertyName::Literal(*ident), + default_init: Some(expr.clone()), + }); + } + } + } + if object.properties().is_empty() { + bindings.push(PatternObjectElement::Empty); + } + Some(PatternObject::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(PatternArrayElement::Elision); + continue; + }; + match expr { + Expression::Identifier(ident) => { + if strict && ident.sym() == Sym::ARGUMENTS { + return None; + } + + bindings.push(PatternArrayElement::SingleName { + ident: ident.sym(), + default_init: None, + }); + } + Expression::Spread(spread) => { + match spread.val() { + Expression::Identifier(ident) => { + bindings.push(PatternArrayElement::SingleNameRest { ident: ident.sym() }); + } + Expression::PropertyAccess(access) => { + bindings.push(PatternArrayElement::PropertyAccessRest { + access: access.clone(), + }); + } + Expression::ArrayLiteral(array) => { + let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); + bindings.push(PatternArrayElement::PatternRest { pattern }); + } + Expression::ObjectLiteral(object) => { + let pattern = object_decl_to_declaration_pattern(object, strict)?.into(); + bindings.push(PatternArrayElement::PatternRest { pattern }); + } + _ => return None, + } + if i + 1 != array.as_ref().len() { + return None; + } + } + Expression::Assign(assign) => match assign.lhs() { + AssignTarget::Identifier(ident) => { + bindings.push(PatternArrayElement::SingleName { + ident: ident.sym(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Property(access) => { + bindings.push(PatternArrayElement::PropertyAccess { + access: access.clone(), + }); + } + AssignTarget::Pattern(pattern) => match pattern { + Pattern::Object(pattern) => { + bindings.push(PatternArrayElement::Pattern { + pattern: Pattern::Object(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + Pattern::Array(pattern) => { + bindings.push(PatternArrayElement::Pattern { + pattern: Pattern::Array(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + }, + AssignTarget::PrivateProperty(_) | AssignTarget::SuperProperty(_) => return None, + }, + Expression::ArrayLiteral(array) => { + let pattern = array_decl_to_declaration_pattern(array, strict)?.into(); + bindings.push(PatternArrayElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::ObjectLiteral(object) => { + let pattern = object_decl_to_declaration_pattern(object, strict)?.into(); + bindings.push(PatternArrayElement::Pattern { + pattern, + default_init: None, + }); + } + Expression::PropertyAccess(access) => { + bindings.push(PatternArrayElement::PropertyAccess { + access: access.clone(), + }); + } + _ => return None, + } + } + Some(PatternArray::new(bindings.into())) +} diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs b/boa_engine/src/syntax/ast/expression/operator/assign/op.rs new file mode 100644 index 00000000000..45f1d0e1b03 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/assign/op.rs @@ -0,0 +1,242 @@ +/// An assignment operator assigns a value to its left operand based on the value of its right operand. +/// +/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its +/// left operand. That is, `x = y` assigns the value of `y to x`. +/// +/// There are also compound assignment operators that are shorthand for the operations +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AssignOp { + /// The assignment operator assigns the value of the right operand to the left operand. + /// + /// Syntax: `x = y` + + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment + Assign, + /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable. + /// + /// Syntax: `x += y` + /// + /// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment + Add, + + /// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable. + /// + /// Syntax: `x -= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment + Sub, + + /// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable. + /// + /// Syntax: `x *= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment + Mul, + + /// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable. + /// + /// Syntax: `x /= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment + Div, + + /// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable. + /// + /// Syntax: `x %= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment + Mod, + + /// The exponentiation assignment operator raises the value of a variable to the power of the right operand. + /// + /// Syntax: `x ** y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment + Exp, + + /// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x &= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment + And, + + /// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x |= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment + Or, + + /// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on + /// them and assigns the result to the variable. + /// + /// Syntax: `x ^= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment + Xor, + + /// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable. + /// + /// Syntax: `x <<= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment + Shl, + + /// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. + /// + /// Syntax: `x >>= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment + Shr, + + /// The unsigned right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. + /// + /// Syntax: `x >>>= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment + Ushr, + + /// The logical and assignment operator only assigns if the target variable is truthy. + /// + /// Syntax: `x &&= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment + BoolAnd, + + /// The logical or assignment operator only assigns if the target variable is falsy. + /// + /// Syntax: `x ||= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment + BoolOr, + + /// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined). + /// + /// Syntax: `x ??= y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment + Coalesce, +} + +impl AssignOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::Assign => "=", + Self::Add => "+=", + Self::Sub => "-=", + Self::Mul => "*=", + Self::Exp => "**=", + Self::Div => "/=", + Self::Mod => "%=", + Self::And => "&=", + Self::Or => "|=", + Self::Xor => "^=", + Self::Shl => "<<=", + Self::Shr => ">>=", + Self::Ushr => ">>>=", + Self::BoolAnd => "&&=", + Self::BoolOr => "||=", + Self::Coalesce => "??=", + } + } +} + +impl std::fmt::Display for AssignOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs new file mode 100644 index 00000000000..114426e9208 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs @@ -0,0 +1,62 @@ +pub mod op; + +use boa_interner::{Interner, ToInternedString}; + +use crate::syntax::ast::expression::Expression; + +/// Binary operations require two operands, one before the operator and one after the operator. +/// +/// More information: +/// - [MDN documentation][mdn] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct Binary { + op: op::BinaryOp, + lhs: Box, + rhs: Box, +} + +impl Binary { + /// Creates a `BinOp` AST Expression. + pub(in crate::syntax) fn new(op: op::BinaryOp, lhs: Expression, rhs: Expression) -> Self { + Self { + op, + lhs: Box::new(lhs), + rhs: Box::new(rhs), + } + } + + /// Gets the binary operation of the Expression. + pub fn op(&self) -> op::BinaryOp { + self.op + } + + /// Gets the left hand side of the binary operation. + pub fn lhs(&self) -> &Expression { + &self.lhs + } + + /// Gets the right hand side of the binary operation. + pub fn rhs(&self) -> &Expression { + &self.rhs + } +} + +impl ToInternedString for Binary { + 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 { + fn from(op: Binary) -> Self { + Self::Binary(op) + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs b/boa_engine/src/syntax/ast/expression/operator/binary/op.rs new file mode 100644 index 00000000000..f4cc00b2c59 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/binary/op.rs @@ -0,0 +1,564 @@ +//! This module implements various structure for logic handling. + +use std::fmt::{Display, Formatter, Result}; + +/// This represents a binary operation between two values. +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinaryOp { + /// Numeric operation. + /// + /// see: [`NumOp`](enum.NumOp.html) + Arithmetic(ArithmeticOp), + + /// Bitwise operation. + /// + /// see: [`BitOp`](enum.BitOp.html). + Bitwise(BitwiseOp), + + /// Comparative operation. + /// + /// see: [`CompOp`](enum.CompOp.html). + Relational(RelationalOp), + + /// Logical operation. + /// + /// see: [`LogOp`](enum.LogOp.html). + Logical(LogicalOp), + + /// Comma operation. + Comma, +} + +impl From for BinaryOp { + fn from(op: ArithmeticOp) -> Self { + Self::Arithmetic(op) + } +} + +impl From for BinaryOp { + fn from(op: BitwiseOp) -> Self { + Self::Bitwise(op) + } +} + +impl From for BinaryOp { + fn from(op: RelationalOp) -> Self { + Self::Relational(op) + } +} + +impl From for BinaryOp { + fn from(op: LogicalOp) -> Self { + Self::Logical(op) + } +} + +impl BinaryOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::Arithmetic(ref op) => op.as_str(), + Self::Bitwise(ref op) => op.as_str(), + Self::Relational(ref op) => op.as_str(), + Self::Logical(ref op) => op.as_str(), + Self::Comma => ",", + } + } +} + +impl Display for BinaryOp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// Arithmetic operators take numerical values (either literals or variables) +/// as their operands and return a single numerical value. +/// +/// More information: +/// - [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))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ArithmeticOp { + /// The addition operator produces the sum of numeric operands or string concatenation. + /// + /// Syntax: `x + y` + /// + /// More information: + /// - [ECMAScript reference][spec]. + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition + Add, + + /// The subtraction operator subtracts the two operands, producing their difference. + /// + /// Syntax: `x - y` + /// + /// More information: + /// - [ECMAScript reference][spec]. + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction + Sub, + + /// The division operator produces the quotient of its operands where the left operand + /// is the dividend and the right operand is the divisor. + /// + /// Syntax: `x / y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division + Div, + + /// The multiplication operator produces the product of the operands. + /// + /// Syntax: `x * y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication + Mul, + + /// The exponentiation operator returns the result of raising the first operand to + /// the power of the second operand. + /// + /// Syntax: `x ** y` + /// + /// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c). + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-exp-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation + Exp, + + /// The remainder operator returns the remainder left over when one operand is divided by a second operand. + /// + /// Syntax: `x % y` + /// + /// The remainder operator always takes the sign of the dividend. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder + Mod, +} + +impl ArithmeticOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::Add => "+", + Self::Sub => "-", + Self::Div => "/", + Self::Mul => "*", + Self::Exp => "**", + Self::Mod => "%", + } + } +} + +impl Display for ArithmeticOp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// A bitwise operator is an operator used to perform bitwise operations +/// on bit patterns or binary numerals that involve the manipulation of individual bits. +/// +/// More information: +/// - [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))] +#[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. + /// + /// Syntax: `x & y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND + And, + + /// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1. + /// + /// Syntax: `x | y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR + Or, + + /// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different. + /// + /// Syntax: `x ^ y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR + Xor, + + /// This operator shifts the first operand the specified number of bits to the left. + /// + /// Syntax: `x << y` + /// + /// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift + Shl, + + /// This operator shifts the first operand the specified number of bits to the right. + /// + /// Syntax: `x >> y` + /// + /// Excess bits shifted off to the right are discarded. Copies of the leftmost bit + /// are shifted in from the left. Since the new leftmost bit has the same value as + /// the previous leftmost bit, the sign bit (the leftmost bit) does not change. + /// Hence the name "sign-propagating". + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift + Shr, + + /// This operator shifts the first operand the specified number of bits to the right. + /// + /// Syntax: `x >>> y` + /// + /// Excess bits shifted off to the right are discarded. Zero bits are shifted in + /// from the left. The sign bit becomes 0, so the result is always non-negative. + /// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift + UShr, +} + +impl BitwiseOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::And => "&", + Self::Or => "|", + Self::Xor => "^", + Self::Shl => "<<", + Self::Shr => ">>", + Self::UShr => ">>>", + } + } +} + +impl Display for BitwiseOp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// A relational operator compares its operands and returns a logical value based on whether the relation is true. +/// +/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard +/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type, +/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in +/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==` +/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands +/// to compatible types before checking equality. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[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 + /// strict comparison. + /// + /// Syntax: `y == y` + /// + /// If both operands are objects, then JavaScript compares internal references which are equal + /// when operands refer to the same object in memory. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality + Equal, + + /// The inequality operator returns `true` if the operands are not equal. + /// + /// Syntax: `x != y` + /// + /// If the two operands are not of the same type, JavaScript attempts to convert the operands + /// to an appropriate type for the comparison. If both operands are objects, then JavaScript + /// compares internal references which are not equal when operands refer to different objects + /// in memory. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality + NotEqual, + + /// The identity operator returns `true` if the operands are strictly equal **with no type + /// conversion**. + /// + /// Syntax: `x === y` + /// + /// Returns `true` if the operands are equal and of the same type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity + StrictEqual, + + /// The non-identity operator returns `true` if the operands **are not equal and/or not of the + /// same type**. + /// + /// Syntax: `x !== y` + /// + /// Returns `true` if the operands are of the same type but not equal, or are of different type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity> + StrictNotEqual, + + /// The greater than operator returns `true` if the left operand is greater than the right + /// operand. + /// + /// Syntax: `x > y` + /// + /// Returns `true` if the left operand is greater than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator + GreaterThan, + + /// The greater than or equal operator returns `true` if the left operand is greater than or + /// equal to the right operand. + /// + /// Syntax: `x >= y` + /// + /// Returns `true` if the left operand is greater than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator + GreaterThanOrEqual, + + /// The less than operator returns `true` if the left operand is less than the right operand. + /// + /// Syntax: `x < y` + /// + /// Returns `true` if the left operand is less than the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator + LessThan, + + /// The less than or equal operator returns `true` if the left operand is less than or equal to + /// the right operand. + /// + /// Syntax: `x <= y` + /// + /// Returns `true` if the left operand is less than or equal to the right operand. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator + LessThanOrEqual, + + /// The `in` operator returns `true` if the specified property is in the specified object or + /// its prototype chain. + /// + /// Syntax: `prop in object` + /// + /// Returns `true` the specified property is in the specified object or its prototype chain. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in + In, + + /// The `instanceof` operator returns `true` if the specified object is an instance of the + /// right hand side object. + /// + /// Syntax: `obj instanceof Object` + /// + /// Returns `true` the `prototype` property of the right hand side constructor appears anywhere + /// in the prototype chain of the object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof + InstanceOf, +} + +impl RelationalOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::Equal => "==", + Self::NotEqual => "!=", + Self::StrictEqual => "===", + Self::StrictNotEqual => "!==", + Self::GreaterThan => ">", + Self::GreaterThanOrEqual => ">=", + Self::LessThan => "<", + Self::LessThanOrEqual => "<=", + Self::In => "in", + Self::InstanceOf => "instanceof", + } + } +} + +impl Display for RelationalOp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} + +/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value. +/// +/// However, the `&&` and `||` operators actually return the value of one of the specified operands, +/// so if these operators are used with non-Boolean values, they may return a non-Boolean value. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[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`; + /// otherwise, it returns the second operand. + /// + /// Syntax: `x && y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND + And, + + /// The logical OR operator returns the value the first operand if it can be coerced into `true`; + /// otherwise, it returns the second operand. + /// + /// Syntax: `x || y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR + Or, + + /// The nullish coalescing operator is a logical operator that returns the second operand + /// when its first operand is null or undefined, and otherwise returns its first operand. + /// + /// Syntax: `x ?? y` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator + Coalesce, +} + +impl LogicalOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::And => "&&", + Self::Or => "||", + Self::Coalesce => "??", + } + } +} + +impl Display for LogicalOp { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/conditional.rs b/boa_engine/src/syntax/ast/expression/operator/conditional.rs new file mode 100644 index 00000000000..5d589977d32 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/conditional.rs @@ -0,0 +1,64 @@ +use crate::syntax::ast::expression::Expression; +use boa_interner::{Interner, ToInternedString}; + +/// The `conditional` (ternary) operation is the only JavaScript operation that takes three +/// operands. +/// +/// This operation takes three operands: a condition followed by a question mark (`?`), +/// then an expression to execute `if` the condition is truthy followed by a colon (`:`), +/// and finally the expression to execute if the condition is `false`. +/// This operator is frequently used as a shortcut for the `if` statement. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct Conditional { + condition: Box, + if_true: Box, + if_false: Box, +} + +impl Conditional { + pub fn cond(&self) -> &Expression { + &self.condition + } + + pub fn if_true(&self) -> &Expression { + &self.if_true + } + + pub fn if_false(&self) -> &Expression { + &self.if_false + } + + /// Creates a `ConditionalOp` AST Expression. + pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self { + Self { + condition: Box::new(condition), + if_true: Box::new(if_true), + if_false: Box::new(if_false), + } + } +} + +impl ToInternedString for Conditional { + fn to_interned_string(&self, interner: &Interner) -> String { + format!( + "{} ? {} : {}", + self.cond().to_interned_string(interner), + self.if_true().to_interned_string(interner), + self.if_false().to_interned_string(interner) + ) + } +} + +impl From for Expression { + fn from(cond_op: Conditional) -> Self { + Self::Conditional(cond_op) + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/mod.rs b/boa_engine/src/syntax/ast/expression/operator/mod.rs new file mode 100644 index 00000000000..697dfbaa76f --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/mod.rs @@ -0,0 +1,11 @@ +//! Operator Expressions + +pub mod assign; +pub mod binary; +pub mod conditional; +pub mod unary; + +pub use self::{assign::Assign, binary::Binary, unary::Unary}; + +#[cfg(test)] +mod tests; diff --git a/boa_engine/src/syntax/ast/node/operator/tests.rs b/boa_engine/src/syntax/ast/expression/operator/tests.rs similarity index 98% rename from boa_engine/src/syntax/ast/node/operator/tests.rs rename to boa_engine/src/syntax/ast/expression/operator/tests.rs index f5f9815f469..071fc2eb9a4 100644 --- a/boa_engine/src/syntax/ast/node/operator/tests.rs +++ b/boa_engine/src/syntax/ast/expression/operator/tests.rs @@ -116,7 +116,7 @@ fn logical_assignment() { #[test] fn fmt() { - super::super::test_formatting( + crate::syntax::ast::test_formatting( r#" let a = 20; a += 10; diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs new file mode 100644 index 00000000000..315e55897b3 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs @@ -0,0 +1,60 @@ +pub mod op; + +use boa_interner::{Interner, ToInternedString}; + +use crate::syntax::ast::expression::Expression; + +/// A unary operation is an operation with only one operand. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct Unary { + op: op::UnaryOp, + target: Box, +} + +impl Unary { + /// Creates a new `UnaryOp` AST Expression. + pub(in crate::syntax) fn new(op: op::UnaryOp, target: Expression) -> Self { + Self { + op, + target: Box::new(target), + } + } + + /// Gets the unary operation of the Expression. + pub fn op(&self) -> op::UnaryOp { + self.op + } + + /// Gets the target of this unary operator. + pub fn target(&self) -> &Expression { + self.target.as_ref() + } +} + +impl ToInternedString for Unary { + fn to_interned_string(&self, interner: &Interner) -> String { + let space = match self.op { + op::UnaryOp::TypeOf | op::UnaryOp::Delete | op::UnaryOp::Void => " ", + _ => "", + }; + format!( + "{}{space}{}", + self.op, + self.target.to_interned_string(interner) + ) + } +} + +impl From for Expression { + fn from(op: Unary) -> Self { + Self::Unary(op) + } +} diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs b/boa_engine/src/syntax/ast/expression/operator/unary/op.rs new file mode 100644 index 00000000000..76c53d26ca6 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/operator/unary/op.rs @@ -0,0 +1,214 @@ +/// A unary operator is one that takes a single operand/argument and performs an operation. +/// +/// A unary operation is an operation with only one operand. This operand comes either +/// before or after the operator. Unary operators are more efficient than standard JavaScript +/// function calls. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnaryOp { + /// The increment operator increments (adds one to) its operand and returns a value. + /// + /// Syntax: `++x` + /// + /// This operator increments and returns the value after incrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment + IncrementPost, + + /// The increment operator increments (adds one to) its operand and returns a value. + /// + /// Syntax: `x++` + /// + /// This operator increments and returns the value before incrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment + IncrementPre, + + /// The decrement operator decrements (subtracts one from) its operand and returns a value. + /// + /// Syntax: `--x` + /// + /// This operator decrements and returns the value before decrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement + DecrementPost, + + /// The decrement operator decrements (subtracts one from) its operand and returns a value. + /// + /// Syntax: `x--` + /// + /// This operator decrements the operand and returns the value after decrementing. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement + DecrementPre, + + /// The unary negation operator precedes its operand and negates it. + /// + /// Syntax: `-x` + /// + /// Converts non-numbers data types to numbers like unary plus, + /// however, it performs an additional operation, negation. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation + Minus, + + /// The unary plus operator attempts to convert the operand into a number, if it isn't already. + /// + /// Syntax: `+x` + /// + /// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred + /// way of converting something into a number, because it does not perform any other operations on the number. + /// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus + Plus, + + /// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`. + /// + /// Syntax: `!x` + /// + /// Boolean values simply get inverted: `!true === false` and `!false === true`. + /// Non-boolean values get converted to boolean values first, then are negated. + /// This means that it is possible to use a couple of NOT operators in series to explicitly + /// force the conversion of any value to the corresponding boolean primitive. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT + Not, + + /// Performs the NOT operator on each bit. + /// + /// Syntax: `~x` + /// + /// NOT `a` yields the inverted value (or one's complement) of `a`. + /// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT + Tilde, + + /// The `typeof` operator returns a string indicating the type of the unevaluated operand. + /// + /// Syntax: `typeof x` or `typeof(x)` + /// + /// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it. + /// You can use this to validate function parameters or check if variables are defined. + /// There are other uses as well. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof + TypeOf, + + /// The JavaScript `delete` operator removes a property from an object. + /// + /// Syntax: `delete x` + /// + /// Unlike what common belief suggests, the delete operator has nothing to do with + /// directly freeing memory. Memory management is done indirectly via breaking references. + /// If no more references to the same property are held, it is eventually released automatically. + /// + /// The `delete` operator returns `true` for all cases except when the property is an + /// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) + /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete) + /// property, in which case, `false` is returned in non-strict mode. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-delete-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete + Delete, + + /// The `void` operator evaluates the given `expression` and then returns `undefined`. + /// + /// Syntax: `void x` + /// + /// This operator allows evaluating expressions that produce a value into places where an + /// expression that evaluates to `undefined` is desired. + /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)` + /// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used. + /// + /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), + /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-void-operator + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + Void, +} + +impl UnaryOp { + /// Retrieves the operation as a static string. + fn as_str(self) -> &'static str { + match self { + Self::IncrementPost | Self::IncrementPre => "++", + Self::DecrementPost | Self::DecrementPre => "--", + Self::Plus => "+", + Self::Minus => "-", + Self::Not => "!", + Self::Tilde => "~", + Self::Delete => "delete", + Self::TypeOf => "typeof", + Self::Void => "void", + } + } +} + +impl std::fmt::Display for UnaryOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} diff --git a/boa_engine/src/syntax/ast/expression/spread.rs b/boa_engine/src/syntax/ast/expression/spread.rs new file mode 100644 index 00000000000..e11d9edf2b6 --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/spread.rs @@ -0,0 +1,100 @@ +use boa_interner::{Interner, ToInternedString}; + +use super::Expression; + +/// The `spread` operator allows an iterable such as an array expression or string to be +/// expanded. +/// +/// Syntax: `...x` +/// +/// It expands array expressions or strings in places where zero or more arguments (for +/// function calls) or elements (for array literals) +/// are expected, or an object expression to be expanded in places where zero or more key-value +/// pairs (for object literals) are expected. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct Spread { + val: Box, +} + +impl Spread { + pub fn val(&self) -> &Expression { + &self.val + } + + /// Creates a `Spread` AST Expression. + pub fn new(val: Expression) -> Self { + Self { val: Box::new(val) } + } +} + +impl ToInternedString for Spread { + fn to_interned_string(&self, interner: &Interner) -> String { + format!("...{}", self.val().to_interned_string(interner)) + } +} + +impl From for Expression { + fn from(spread: Spread) -> Self { + Self::Spread(spread) + } +} + +#[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_engine/src/syntax/ast/expression/tagged_template.rs new file mode 100644 index 00000000000..51f5d52a7db --- /dev/null +++ b/boa_engine/src/syntax/ast/expression/tagged_template.rs @@ -0,0 +1,91 @@ +use boa_interner::{Interner, Sym, ToInternedString}; + +use super::Expression; + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct TaggedTemplate { + tag: Box, + raws: Box<[Sym]>, + cookeds: Box<[Option]>, + exprs: Box<[Expression]>, +} + +impl TaggedTemplate { + /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and + /// the expressions. + pub fn new( + tag: Expression, + raws: Box<[Sym]>, + cookeds: Box<[Option]>, + exprs: Box<[Expression]>, + ) -> Self { + Self { + tag: tag.into(), + raws, + cookeds, + exprs, + } + } + + pub(crate) fn tag(&self) -> &Expression { + &self.tag + } + + pub(crate) fn raws(&self) -> &[Sym] { + &self.raws + } + + pub(crate) fn cookeds(&self) -> &[Option] { + &self.cookeds + } + + pub(crate) fn exprs(&self) -> &[Expression] { + &self.exprs + } +} + +impl ToInternedString for TaggedTemplate { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = format!("{}`", self.tag.to_interned_string(interner)); + for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { + buf.push_str(&format!( + "{}${{{}}}", + interner.resolve_expect(raw), + expr.to_interned_string(interner) + )); + } + buf.push('`'); + + buf + } +} + +impl From for Expression { + fn from(template: TaggedTemplate) -> Self { + Self::TaggedTemplate(template) + } +} + +#[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/node/yield/mod.rs b/boa_engine/src/syntax/ast/expression/yield.rs similarity index 69% rename from boa_engine/src/syntax/ast/node/yield/mod.rs rename to boa_engine/src/syntax/ast/expression/yield.rs index 16ff56979e2..837a4535e95 100644 --- a/boa_engine/src/syntax/ast/node/yield/mod.rs +++ b/boa_engine/src/syntax/ast/expression/yield.rs @@ -1,8 +1,6 @@ -use crate::syntax::ast::node::Node; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::Expression; /// The `yield` keyword is used to pause and resume a generator function /// @@ -12,15 +10,15 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Yield { - expr: Option>, + expr: Option>, delegate: bool, } impl Yield { - pub fn expr(&self) -> Option<&Node> { + pub fn expr(&self) -> Option<&Expression> { self.expr.as_ref().map(Box::as_ref) } @@ -28,19 +26,16 @@ impl Yield { self.delegate } - /// Creates a `Yield` AST node. - pub fn new(expr: Option, delegate: bool) -> Self - where - E: Into, - { + /// Creates a `Yield` AST Expression. + pub fn new(expr: Option, delegate: bool) -> Self { Self { - expr: expr.map(Into::into).map(Box::new), + expr: expr.map(Box::new), delegate, } } } -impl From for Node { +impl From for Expression { fn from(r#yield: Yield) -> Self { Self::Yield(r#yield) } diff --git a/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa_engine/src/syntax/ast/function/arrow_function.rs similarity index 56% rename from boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs rename to boa_engine/src/syntax/ast/function/arrow_function.rs index ab0d04fcf19..f5885b22a8c 100644 --- a/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa_engine/src/syntax/ast/function/arrow_function.rs @@ -1,8 +1,9 @@ -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; +use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::join_nodes; +use crate::syntax::ast::statement::StatementList; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::FormalParameterList; /// An arrow function expression is a syntactically compact alternative to a regular function /// expression. @@ -17,26 +18,25 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct ArrowFunctionDecl { +pub struct ArrowFunction { name: Option, - params: FormalParameterList, + parameters: FormalParameterList, body: StatementList, } -impl ArrowFunctionDecl { - /// Creates a new `ArrowFunctionDecl` AST node. - pub(in crate::syntax) fn new(name: N, params: P, body: B) -> Self - where - N: Into>, - P: Into, - B: Into, - { +impl ArrowFunction { + /// Creates a new `ArrowFunctionDecl` AST Expression. + pub(in crate::syntax) fn new( + name: Option, + params: FormalParameterList, + body: StatementList, + ) -> Self { Self { - name: name.into(), - params: params.into(), - body: body.into(), + name, + parameters: params, + body, } } @@ -51,8 +51,8 @@ impl ArrowFunctionDecl { } /// Gets the list of parameters of the arrow function. - pub(crate) fn params(&self) -> &FormalParameterList { - &self.params + pub(crate) fn parameters(&self) -> &FormalParameterList { + &self.parameters } /// Gets the body of the arrow function. @@ -61,13 +61,9 @@ impl ArrowFunctionDecl { } /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = format!("({}", join_nodes(interner, &self.params.parameters)); - if self.body().items().is_empty() { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = format!("({}", join_nodes(interner, &self.parameters.parameters)); + if self.body().statements().is_empty() { buf.push_str(") => {}"); } else { buf.push_str(&format!( @@ -80,14 +76,30 @@ impl ArrowFunctionDecl { } } -impl ToInternedString for ArrowFunctionDecl { +impl ToInternedString for ArrowFunction { fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } } -impl From for Node { - fn from(decl: ArrowFunctionDecl) -> Self { - Self::ArrowFunctionDecl(decl) +impl From for Expression { + fn from(decl: ArrowFunction) -> Self { + Self::ArrowFunction(decl) + } +} + +#[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/node/declaration/async_function_expr/mod.rs b/boa_engine/src/syntax/ast/function/async_function.rs similarity index 57% rename from boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs rename to boa_engine/src/syntax/ast/function/async_function.rs index c1e5eb6fd77..ea32c6fa43c 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs +++ b/boa_engine/src/syntax/ast/function/async_function.rs @@ -1,10 +1,11 @@ //! Async Function Expression. -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; +use crate::syntax::ast::join_nodes; +use crate::syntax::ast::statement::StatementList; +use crate::syntax::ast::{expression::Expression, Statement}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::FormalParameterList; /// An async function expression is very similar to an async function declaration except used within /// a wider expression (for example during an assignment). @@ -15,26 +16,25 @@ use serde::{Deserialize, Serialize}; /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct AsyncFunctionExpr { +pub struct AsyncFunction { name: Option, parameters: FormalParameterList, body: StatementList, } -impl AsyncFunctionExpr { +impl AsyncFunction { /// Creates a new function expression - pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self - where - N: Into>, - P: Into, - B: Into, - { + pub(in crate::syntax) fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { Self { - name: name.into(), - parameters: parameters.into(), - body: body.into(), + name, + parameters, + body, } } @@ -54,11 +54,7 @@ impl AsyncFunctionExpr { } /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function".to_owned(); if let Some(name) = self.name { buf.push_str(&format!(" {}", interner.resolve_expect(name))); @@ -67,7 +63,7 @@ impl AsyncFunctionExpr { "({}", join_nodes(interner, &self.parameters.parameters) )); - if self.body().items().is_empty() { + if self.body().statements().is_empty() { buf.push_str(") {}"); } else { buf.push_str(&format!( @@ -80,14 +76,39 @@ impl AsyncFunctionExpr { } } -impl ToInternedString for AsyncFunctionExpr { +impl ToInternedString for AsyncFunction { fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } } -impl From for Node { - fn from(expr: AsyncFunctionExpr) -> Self { - Self::AsyncFunctionExpr(expr) +impl From for Expression { + fn from(expr: AsyncFunction) -> Self { + Self::AsyncFunction(expr) + } +} + +impl From for Statement { + fn from(f: AsyncFunction) -> Self { + Self::AsyncFunction(f) + } +} + +#[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/node/declaration/async_generator_expr/mod.rs b/boa_engine/src/syntax/ast/function/async_generator.rs similarity index 60% rename from boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs rename to boa_engine/src/syntax/ast/function/async_generator.rs index 95b267bcb96..707acd3f4dc 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs +++ b/boa_engine/src/syntax/ast/function/async_generator.rs @@ -1,12 +1,10 @@ //! Async Generator Expression - -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; +use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::statement::StatementList; +use crate::syntax::ast::{block_to_string, join_nodes, Statement}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -use super::block_to_string; +use super::FormalParameterList; /// The `async function*` keyword can be used to define a generator function inside an expression. /// @@ -14,26 +12,25 @@ use super::block_to_string; /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct AsyncGeneratorExpr { +pub struct AsyncGenerator { name: Option, parameters: FormalParameterList, body: StatementList, } -impl AsyncGeneratorExpr { +impl AsyncGenerator { /// Creates a new async generator expression - pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self - where - N: Into>, - P: Into, - B: Into, - { + pub(in crate::syntax) fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { Self { - name: name.into(), - parameters: parameters.into(), - body: body.into(), + name, + parameters, + body, } } @@ -52,11 +49,7 @@ impl AsyncGeneratorExpr { &self.body } - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function*".to_owned(); if let Some(name) = self.name { buf.push_str(&format!(" {}", interner.resolve_expect(name))); @@ -71,14 +64,20 @@ impl AsyncGeneratorExpr { } } -impl ToInternedString for AsyncGeneratorExpr { +impl ToInternedString for AsyncGenerator { fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } } -impl From for Node { - fn from(expr: AsyncGeneratorExpr) -> Self { - Self::AsyncGeneratorExpr(expr) +impl From for Expression { + fn from(expr: AsyncGenerator) -> Self { + Self::AsyncGenerator(expr) + } +} + +impl From for Statement { + fn from(f: AsyncGenerator) -> Self { + Self::AsyncGenerator(f) } } diff --git a/boa_engine/src/syntax/ast/function/class.rs b/boa_engine/src/syntax/ast/function/class.rs new file mode 100644 index 00000000000..ed0db402fae --- /dev/null +++ b/boa_engine/src/syntax/ast/function/class.rs @@ -0,0 +1,527 @@ +use std::borrow::Cow; + +use crate::string::ToStringEscaped; +use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::property::{MethodDefinition, PropertyName}; +use crate::syntax::ast::statement::StatementList; +use crate::syntax::ast::{block_to_string, join_nodes, ContainsSymbol, Statement}; +use boa_interner::{Interner, Sym, ToInternedString}; + +use super::Function; + +/// The `class` declaration defines a class with the specified methods, fields, and optional constructor. +/// +/// Classes can be used to create objects, which can also be created through literals (using `{}`). +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-class-definitions +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct Class { + name: Option, + super_ref: Option, + constructor: Option, + elements: Box<[ClassElement]>, +} + +impl Class { + /// Creates a new class declaration. + pub(in crate::syntax) fn new( + name: Option, + super_ref: Option, + constructor: Option, + elements: Box<[ClassElement]>, + ) -> Self { + Self { + name, + super_ref, + constructor, + elements, + } + } + + /// Returns the name of the class. + pub(crate) fn name(&self) -> Option { + self.name + } + + /// Returns the super class ref of the class. + pub(crate) fn super_ref(&self) -> Option<&Expression> { + self.super_ref.as_ref() + } + + /// Returns the constructor of the class. + pub(crate) fn constructor(&self) -> Option<&Function> { + self.constructor.as_ref() + } + + /// Gets the list of all fields defined on the class. + pub(crate) fn elements(&self) -> &[ClassElement] { + &self.elements + } + + /// Implements the display formatting with indentation. + pub(crate) fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { + let class_name = self.name.map_or(Cow::Borrowed(""), |s| { + interner.resolve_expect(s).join( + Cow::Borrowed, + |utf16| Cow::Owned(utf16.to_string_escaped()), + true, + ) + }); + if self.elements.is_empty() && self.constructor().is_none() { + return format!( + "class {class_name}{} {{}}", + if let Some(sup) = &self.super_ref { + format!(" extends {}", sup.to_interned_string(interner)) + } else { + "".to_string() + } + ); + } + let indentation = " ".repeat(indent_n + 1); + let mut buf = format!( + "class {class_name}{} {{\n", + if let Some(sup) = &self.super_ref { + format!("extends {}", sup.to_interned_string(interner)) + } else { + "".to_string() + } + ); + if let Some(expr) = &self.constructor { + buf.push_str(&format!( + "{indentation}constructor({}) {}\n", + join_nodes(interner, &expr.parameters().parameters), + block_to_string(expr.body(), interner, indent_n + 1) + )); + } + for element in self.elements.iter() { + buf.push_str(&match element { + ClassElement::MethodDefinition(name, method) => { + format!( + "{indentation}{}{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + name.to_interned_string(interner), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::StaticMethodDefinition(name, method) => { + format!( + "{indentation}static {}{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + name.to_interned_string(interner), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::FieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}{} = {};\n", + name.to_interned_string(interner), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!("{indentation}{};\n", name.to_interned_string(interner),) + } + }, + ClassElement::StaticFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}static {} = {};\n", + name.to_interned_string(interner), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!( + "{indentation}static {};\n", + name.to_interned_string(interner), + ) + } + }, + ClassElement::PrivateMethodDefinition(name, method) => { + format!( + "{indentation}{}#{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + interner.resolve_expect(*name), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::PrivateStaticMethodDefinition(name, method) => { + format!( + "{indentation}static {}#{}({}) {}\n", + match &method { + MethodDefinition::Get(_) => "get ", + MethodDefinition::Set(_) => "set ", + _ => "", + }, + interner.resolve_expect(*name), + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Generator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::AsyncGenerator(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + MethodDefinition::Async(expr) => { + join_nodes(interner, &expr.parameters().parameters) + } + }, + match &method { + MethodDefinition::Get(expr) + | MethodDefinition::Set(expr) + | MethodDefinition::Ordinary(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Generator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::AsyncGenerator(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + MethodDefinition::Async(expr) => { + block_to_string(expr.body(), interner, indent_n + 1) + } + }, + ) + } + ClassElement::PrivateFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}#{} = {};\n", + interner.resolve_expect(*name), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!("{indentation}#{};\n", interner.resolve_expect(*name),) + } + }, + ClassElement::PrivateStaticFieldDefinition(name, field) => match field { + Some(expr) => { + format!( + "{indentation}static #{} = {};\n", + interner.resolve_expect(*name), + expr.to_no_indent_string(interner, indent_n + 1) + ) + } + None => { + format!("{indentation}static #{};\n", interner.resolve_expect(*name),) + } + }, + ClassElement::StaticBlock(statement_list) => { + format!( + "{indentation}static {}\n", + block_to_string(statement_list, interner, indent_n + 1) + ) + } + }); + } + buf.push('}'); + buf + } +} + +impl ToInternedString for Class { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for Expression { + fn from(expr: Class) -> Self { + Self::Class(Box::new(expr)) + } +} + +impl From for Statement { + fn from(f: Class) -> Self { + Self::Class(f) + } +} + +/// Class element types. +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum ClassElement { + MethodDefinition(PropertyName, MethodDefinition), + StaticMethodDefinition(PropertyName, MethodDefinition), + FieldDefinition(PropertyName, Option), + StaticFieldDefinition(PropertyName, Option), + PrivateMethodDefinition(Sym, MethodDefinition), + PrivateStaticMethodDefinition(Sym, MethodDefinition), + PrivateFieldDefinition(Sym, Option), + PrivateStaticFieldDefinition(Sym, Option), + StaticBlock(StatementList), +} + +impl ClassElement { + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Self::MethodDefinition(_, method) | Self::StaticMethodDefinition(_, method) => { + match method { + MethodDefinition::Get(function) + | MethodDefinition::Set(function) + | MethodDefinition::Ordinary(function) => { + matches!(function.name(), Some(Sym::ARGUMENTS)) + } + MethodDefinition::Generator(generator) => { + matches!(generator.name(), Some(Sym::ARGUMENTS)) + } + MethodDefinition::AsyncGenerator(async_generator) => { + matches!(async_generator.name(), Some(Sym::ARGUMENTS)) + } + MethodDefinition::Async(function) => { + matches!(function.name(), Some(Sym::ARGUMENTS)) + } + } + } + Self::FieldDefinition(_, Some(node)) + | Self::StaticFieldDefinition(_, Some(node)) + | Self::PrivateFieldDefinition(_, Some(node)) + | Self::PrivateStaticFieldDefinition(_, Some(node)) => node.contains_arguments(), + Self::StaticBlock(statement_list) => statement_list + .statements() + .iter() + .any(Statement::contains_arguments), + _ => false, + } + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::MethodDefinition(name, _) + | Self::StaticMethodDefinition(name, _) + | Self::FieldDefinition(name, _) + | Self::StaticFieldDefinition(name, _) => { + matches!(name.computed(), Some(expr) if expr.contains(symbol)) + } + _ => false, + } + } +} + +#[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/node/declaration/generator_expr/mod.rs b/boa_engine/src/syntax/ast/function/generator.rs similarity index 60% rename from boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs rename to boa_engine/src/syntax/ast/function/generator.rs index 33778f779aa..ad58b1d4521 100644 --- a/boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs +++ b/boa_engine/src/syntax/ast/function/generator.rs @@ -1,10 +1,10 @@ -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; +use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{block_to_string, join_nodes, Statement}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use crate::syntax::ast::statement::StatementList; +use boa_interner::{Interner, Sym, ToInternedString}; -use super::block_to_string; +use super::FormalParameterList; /// The `function*` keyword can be used to define a generator function inside an expression. /// @@ -14,26 +14,25 @@ use super::block_to_string; /// /// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function* -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] -pub struct GeneratorExpr { +pub struct Generator { name: Option, parameters: FormalParameterList, body: StatementList, } -impl GeneratorExpr { +impl Generator { /// Creates a new generator expression - pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self - where - N: Into>, - P: Into, - B: Into, - { + pub(in crate::syntax) fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { Self { - name: name.into(), - parameters: parameters.into(), - body: body.into(), + name, + parameters, + body, } } @@ -52,12 +51,8 @@ impl GeneratorExpr { &self.body } - /// Converts the generator expresion node to a string with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + /// Converts the generator expresion Expression to a string with indentation. + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "function*".to_owned(); if let Some(name) = self.name { buf.push_str(&format!(" {}", interner.resolve_expect(name))); @@ -72,14 +67,20 @@ impl GeneratorExpr { } } -impl ToInternedString for GeneratorExpr { +impl ToInternedString for Generator { fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } } -impl From for Node { - fn from(expr: GeneratorExpr) -> Self { - Self::GeneratorExpr(expr) +impl From for Expression { + fn from(expr: Generator) -> Self { + Self::Generator(expr) + } +} + +impl From for Statement { + fn from(f: Generator) -> Self { + Self::Generator(f) } } diff --git a/boa_engine/src/syntax/ast/function/mod.rs b/boa_engine/src/syntax/ast/function/mod.rs new file mode 100644 index 00000000000..a412646c359 --- /dev/null +++ b/boa_engine/src/syntax/ast/function/mod.rs @@ -0,0 +1,181 @@ +mod arrow_function; +mod async_function; +mod async_generator; +mod class; +mod generator; +mod parameters; + +pub use arrow_function::ArrowFunction; +pub use async_function::AsyncFunction; +pub use async_generator::AsyncGenerator; +pub use class::{Class, ClassElement}; +pub use generator::Generator; +pub use parameters::{FormalParameter, FormalParameterList}; + +pub(crate) use parameters::FormalParameterListFlags; + +use crate::syntax::ast::statement::StatementList; +use crate::syntax::ast::{block_to_string, join_nodes}; +use boa_interner::{Interner, Sym, ToInternedString}; + +use super::expression::Expression; +use super::{ContainsSymbol, Statement}; + +/// The `function` expression defines a function with the specified parameters. +/// +/// A function created with a function expression is a `Function` object and has all the +/// properties, methods and behavior of `Function`. +/// +/// A function can also be created using a declaration (see function expression). +/// +/// By default, functions return `undefined`. To return any other value, the function must have +/// a return statement that specifies the value to return. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct Function { + name: Option, + parameters: FormalParameterList, + body: StatementList, +} + +impl Function { + /// Creates a new function expression + pub(in crate::syntax) fn new( + name: Option, + parameters: FormalParameterList, + body: StatementList, + ) -> Self { + Self { + name, + parameters, + body, + } + } + + /// Gets the name of the function declaration. + pub fn name(&self) -> Option { + self.name + } + + /// Gets the list of parameters of the function declaration. + pub fn parameters(&self) -> &FormalParameterList { + &self.parameters + } + + /// Gets the body of the function declaration. + pub fn body(&self) -> &StatementList { + &self.body + } + + /// Implements the display formatting with indentation. + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = "function".to_owned(); + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name))); + } + buf.push_str(&format!( + "({}) {}", + join_nodes(interner, &self.parameters.parameters), + block_to_string(&self.body, interner, indentation) + )); + + buf + } +} + +impl ToInternedString for Function { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for Expression { + fn from(expr: Function) -> Self { + Self::Function(expr) + } +} + +impl From for Statement { + fn from(f: Function) -> Self { + Self::Function(f) + } +} + +/// 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.declaration().contains(ContainsSymbol::SuperCall) + || param.declaration().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.declaration().contains(ContainsSymbol::SuperCall) { + return true; + } + } + for node in body.statements() { + if node.contains(ContainsSymbol::SuperCall) { + return true; + } + } + false +} + +#[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/node/parameters.rs b/boa_engine/src/syntax/ast/function/parameters.rs similarity index 91% rename from boa_engine/src/syntax/ast/node/parameters.rs rename to boa_engine/src/syntax/ast/function/parameters.rs index 164aa83a1f0..8a127c7260f 100644 --- a/boa_engine/src/syntax/ast/node/parameters.rs +++ b/boa_engine/src/syntax/ast/function/parameters.rs @@ -1,7 +1,9 @@ use crate::syntax::{ ast::{ - node::{ContainsSymbol, Declaration, DeclarationPattern, Node}, - Position, + expression::Expression, + pattern::Pattern, + statement::declaration::{Binding, Declaration}, + ContainsSymbol, Position, }, parser::ParseError, }; @@ -9,16 +11,13 @@ use bitflags::bitflags; use boa_interner::{Interner, Sym, ToInternedString}; use rustc_hash::FxHashSet; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - /// `FormalParameterList` is a list of `FormalParameter`s that describes the parameters of a function. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct FormalParameterList { pub(crate) parameters: Box<[FormalParameter]>, @@ -206,7 +205,7 @@ impl From for FormalParameterList { bitflags! { /// Flags for a [`FormalParameterList`]. #[allow(clippy::unsafe_derive_deserialize)] - #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] pub(crate) struct FormalParameterListFlags: u8 { const IS_SIMPLE = 0b0000_0001; const HAS_DUPLICATES = 0b0000_0010; @@ -237,7 +236,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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct FormalParameter { declaration: Declaration, @@ -258,12 +257,12 @@ impl FormalParameter { /// Gets the name of the formal parameter. pub fn names(&self) -> Vec { - match &self.declaration { - Declaration::Identifier { ident, .. } => vec![ident.sym()], - Declaration::Pattern(pattern) => match pattern { - DeclarationPattern::Object(object_pattern) => object_pattern.idents(), + match self.declaration.binding() { + Binding::Identifier(ident) => vec![ident.sym()], + Binding::Pattern(pattern) => match pattern { + Pattern::Object(object_pattern) => object_pattern.idents(), - DeclarationPattern::Array(array_pattern) => array_pattern.idents(), + Pattern::Array(array_pattern) => array_pattern.idents(), }, } } @@ -274,7 +273,7 @@ impl FormalParameter { } /// Gets the initialization node of the formal parameter, if any. - pub fn init(&self) -> Option<&Node> { + pub fn init(&self) -> Option<&Expression> { self.declaration.init() } @@ -284,7 +283,7 @@ impl FormalParameter { } pub fn is_identifier(&self) -> bool { - matches!(&self.declaration, Declaration::Identifier { .. }) + matches!(&self.declaration.binding(), Binding::Identifier(_)) } } diff --git a/boa_engine/src/syntax/ast/keyword.rs b/boa_engine/src/syntax/ast/keyword.rs index b5c754da5c0..ef6ef20a3aa 100644 --- a/boa_engine/src/syntax/ast/keyword.rs +++ b/boa_engine/src/syntax/ast/keyword.rs @@ -9,14 +9,11 @@ use crate::{ string::utf16, - syntax::ast::op::{BinOp, CompOp}, + syntax::ast::expression::operator::binary::op::{BinaryOp, RelationalOp}, }; use boa_interner::{Interner, Sym}; use std::{convert::TryInto, error, fmt, str::FromStr}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - /// Keywords are tokens that have special meaning in JavaScript. /// /// In JavaScript you cannot use these reserved words as variables, labels, or function names. @@ -27,7 +24,7 @@ use serde::{Deserialize, Serialize}; /// /// [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Keyword { /// The `await` keyword. @@ -486,10 +483,10 @@ pub enum Keyword { impl Keyword { /// Gets the keyword as a binary operation, if this keyword is the `in` keyword. - pub fn as_binop(self) -> Option { + pub fn as_binary_op(self) -> Option { match self { - Self::In => Some(BinOp::Comp(CompOp::In)), - Self::InstanceOf => Some(BinOp::Comp(CompOp::InstanceOf)), + Self::In => Some(BinaryOp::Relational(RelationalOp::In)), + Self::InstanceOf => Some(BinaryOp::Relational(RelationalOp::InstanceOf)), _ => None, } } @@ -548,10 +545,10 @@ impl Keyword { } } -impl TryInto for Keyword { +impl TryInto for Keyword { type Error = String; - fn try_into(self) -> Result { - self.as_binop() + fn try_into(self) -> Result { + self.as_binary_op() .ok_or_else(|| format!("No binary operation for {self}")) } } diff --git a/boa_engine/src/syntax/ast/mod.rs b/boa_engine/src/syntax/ast/mod.rs index b768c092460..af028ebd52b 100644 --- a/boa_engine/src/syntax/ast/mod.rs +++ b/boa_engine/src/syntax/ast/mod.rs @@ -1,16 +1,106 @@ //! The Javascript Abstract Syntax Tree. -pub mod constant; +pub mod expression; +pub mod function; pub mod keyword; -pub mod node; -pub mod op; +pub mod pattern; pub mod position; +pub mod property; pub mod punctuator; +pub mod statement; +use boa_interner::{Interner, ToInternedString}; + +use self::statement::StatementList; pub use self::{ - constant::Const, + expression::Expression, keyword::Keyword, - node::Node, position::{Position, Span}, punctuator::Punctuator, + statement::Statement, }; + +/// Represents the possible symbols that can be use the the `contains` function. +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum ContainsSymbol { + SuperProperty, + SuperCall, + YieldExpression, + AwaitExpression, + NewTarget, +} + +/// Utility to join multiple Nodes into a single string. +fn join_nodes(interner: &Interner, nodes: &[N]) -> String +where + N: ToInternedString, +{ + let mut first = true; + let mut buf = String::new(); + for e in nodes { + if first { + first = false; + } else { + buf.push_str(", "); + } + buf.push_str(&e.to_interned_string(interner)); + } + buf +} + +/// Displays the body of a block or statement list. +/// +/// This includes the curly braces at the start and end. This will not indent the first brace, +/// but will indent the last brace. +fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize) -> String { + if body.statements().is_empty() { + "{}".to_owned() + } else { + format!( + "{{\n{}{}}}", + body.to_indented_string(interner, indentation + 1), + " ".repeat(indentation) + ) + } +} + +/// 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(); + + 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/ast/node/array/tests.rs b/boa_engine/src/syntax/ast/node/array/tests.rs deleted file mode 100644 index bc14e7b6f80..00000000000 --- a/boa_engine/src/syntax/ast/node/array/tests.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - let a = [1, 2, 3, "words", "more words"]; - let b = []; - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/await_expr/tests.rs b/boa_engine/src/syntax/ast/node/await_expr/tests.rs deleted file mode 100644 index b1b30504ae6..00000000000 --- a/boa_engine/src/syntax/ast/node/await_expr/tests.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[test] -fn fmt() { - // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented. - super::super::test_formatting( - r#" - await function_call(); - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/block/tests.rs b/boa_engine/src/syntax/ast/node/block/tests.rs deleted file mode 100644 index b81cba9b846..00000000000 --- a/boa_engine/src/syntax/ast/node/block/tests.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[test] -fn fmt() { - super::super::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/node/call/mod.rs b/boa_engine/src/syntax/ast/node/call/mod.rs deleted file mode 100644 index c830e6a3f9a..00000000000 --- a/boa_engine/src/syntax/ast/node/call/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::syntax::ast::node::{join_nodes, Node}; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - -/// Calling the function actually performs the specified actions with the indicated parameters. -/// -/// Defining a function does not execute it. Defining it simply names the function and -/// specifies what to do when the function is called. Functions must be in scope when they are -/// called, but the function declaration can be hoisted. The scope of a function is the -/// function in which it is declared (or the entire program, if it is declared at the top -/// level). -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct Call { - expr: Box, - args: Box<[Node]>, -} - -impl Call { - /// Creates a new `Call` AST node. - pub fn new(expr: E, args: A) -> Self - where - E: Into, - A: Into>, - { - Self { - expr: Box::new(expr.into()), - args: args.into(), - } - } - - /// Gets the name of the function call. - pub fn expr(&self) -> &Node { - &self.expr - } - - /// Retrieves the arguments passed to the function. - pub fn args(&self) -> &[Node] { - &self.args - } -} - -impl ToInternedString for Call { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{}({})", - self.expr.to_interned_string(interner), - join_nodes(interner, &self.args) - ) - } -} - -impl From for Node { - fn from(call: Call) -> Self { - Self::Call(call) - } -} diff --git a/boa_engine/src/syntax/ast/node/call/tests.rs b/boa_engine/src/syntax/ast/node/call/tests.rs deleted file mode 100644 index e3dd74d85aa..00000000000 --- a/boa_engine/src/syntax/ast/node/call/tests.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - call_1(1, 2, 3); - call_2("argument here"); - call_3(); - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs b/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs deleted file mode 100644 index 754b191add7..00000000000 --- a/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `conditional` (ternary) operator is the only JavaScript operator that takes three -/// operands. -/// -/// This operator is the only JavaScript operator that takes three operands: a condition -/// followed by a question mark (`?`), then an expression to execute `if` the condition is -/// truthy followed by a colon (`:`), and finally the expression to execute if the condition -/// is `false`. This operator is frequently used as a shortcut for the `if` statement. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct ConditionalOp { - condition: Box, - if_true: Box, - if_false: Box, -} - -impl ConditionalOp { - pub fn cond(&self) -> &Node { - &self.condition - } - - pub fn if_true(&self) -> &Node { - &self.if_true - } - - pub fn if_false(&self) -> &Node { - &self.if_false - } - - /// Creates a `ConditionalOp` AST node. - pub fn new(condition: C, if_true: T, if_false: F) -> Self - where - C: Into, - T: Into, - F: Into, - { - Self { - condition: Box::new(condition.into()), - if_true: Box::new(if_true.into()), - if_false: Box::new(if_false.into()), - } - } -} - -impl ToInternedString for ConditionalOp { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{} ? {} : {}", - self.cond().to_interned_string(interner), - self.if_true().to_interned_string(interner), - self.if_false().to_interned_string(interner) - ) - } -} - -impl From for Node { - fn from(cond_op: ConditionalOp) -> Self { - Self::ConditionalOp(cond_op) - } -} diff --git a/boa_engine/src/syntax/ast/node/conditional/mod.rs b/boa_engine/src/syntax/ast/node/conditional/mod.rs deleted file mode 100644 index 68b1d424db1..00000000000 --- a/boa_engine/src/syntax/ast/node/conditional/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Conditional nodes - -pub mod conditional_op; -pub mod if_node; - -pub use self::{conditional_op::ConditionalOp, if_node::If}; - -#[cfg(test)] -mod tests; diff --git a/boa_engine/src/syntax/ast/node/conditional/tests.rs b/boa_engine/src/syntax/ast/node/conditional/tests.rs deleted file mode 100644 index d1900164645..00000000000 --- a/boa_engine/src/syntax/ast/node/conditional/tests.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - let a = true ? 5 : 6; - if (false) { - a = 10; - } else { - a = 20; - } - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs deleted file mode 100644 index 167ef57b60d..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Async Function Declaration. - -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// An async function is used to specify an action (or series of actions) to perform asynchronously. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-async-function-prototype-properties -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct AsyncFunctionDecl { - name: Sym, - parameters: FormalParameterList, - body: StatementList, -} - -impl AsyncFunctionDecl { - /// Creates a new async function declaration. - pub(in crate::syntax) fn new(name: Sym, parameters: P, body: B) -> Self - where - P: Into, - B: Into, - { - Self { - name, - parameters: parameters.into(), - body: body.into(), - } - } - - /// Gets the name of the async function declaration. - pub fn name(&self) -> Sym { - self.name - } - - /// Gets the list of parameters of the async function declaration. - pub fn parameters(&self) -> &FormalParameterList { - &self.parameters - } - - /// Gets the body of the async function declaration. - pub fn body(&self) -> &StatementList { - &self.body - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = format!( - "async function {}({}", - interner.resolve_expect(self.name), - join_nodes(interner, &self.parameters.parameters) - ); - if self.body.items().is_empty() { - buf.push_str(") {}"); - } else { - buf.push_str(&format!( - ") {{\n{}{}}}", - self.body.to_indented_string(interner, indentation + 1), - " ".repeat(indentation) - )); - } - buf - } -} - -impl From for Node { - fn from(decl: AsyncFunctionDecl) -> Self { - Self::AsyncFunctionDecl(decl) - } -} - -impl ToInternedString for AsyncFunctionDecl { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs deleted file mode 100644 index bd943ac7ec1..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Async Generator Declaration - -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The 'async function*' defines an async generator function -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct AsyncGeneratorDecl { - name: Sym, - parameters: FormalParameterList, - body: StatementList, -} - -impl AsyncGeneratorDecl { - /// Creates a new async generator declaration. - pub(in crate::syntax) fn new(name: Sym, parameters: P, body: B) -> Self - where - P: Into, - B: Into, - { - Self { - name, - parameters: parameters.into(), - body: body.into(), - } - } - - /// Gets the name of the async function declaration. - pub fn name(&self) -> Sym { - self.name - } - - /// Gets the list of parameters of the async function declaration. - pub fn parameters(&self) -> &FormalParameterList { - &self.parameters - } - - /// Gets the body of the async function declaration. - pub fn body(&self) -> &StatementList { - &self.body - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = format!( - "async function* {}({}", - interner.resolve_expect(self.name), - join_nodes(interner, &self.parameters.parameters) - ); - if self.body().items().is_empty() { - buf.push_str(") {}"); - } else { - buf.push_str(&format!( - ") {{\n{}{}}}", - self.body.to_indented_string(interner, indentation + 1), - " ".repeat(indentation) - )); - } - buf - } -} - -impl From for Node { - fn from(decl: AsyncGeneratorDecl) -> Self { - Self::AsyncGeneratorDecl(decl) - } -} - -impl ToInternedString for AsyncGeneratorDecl { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/class_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/class_decl/mod.rs deleted file mode 100644 index 07b5e217367..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/class_decl/mod.rs +++ /dev/null @@ -1,366 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::syntax::ast::node::{ - declaration::{block_to_string, FunctionExpr}, - join_nodes, - object::{MethodDefinition, PropertyName}, - Node, StatementList, -}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `class` declaration defines a class with the specified methods, fields, and optional constructor. -/// -/// Classes can be used to create objects, which can also be created through literals (using `{}`). -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-class-definitions -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct Class { - name: Sym, - super_ref: Option>, - constructor: Option, - elements: Box<[ClassElement]>, -} - -impl Class { - /// Creates a new class declaration. - pub(in crate::syntax) fn new( - name: Sym, - super_ref: S, - constructor: C, - elements: E, - ) -> Self - where - S: Into>>, - C: Into>, - E: Into>, - { - Self { - name, - super_ref: super_ref.into(), - constructor: constructor.into(), - elements: elements.into(), - } - } - - /// Returns the name of the class. - pub(crate) fn name(&self) -> Sym { - self.name - } - - /// Returns the super class ref of the class. - pub(crate) fn super_ref(&self) -> &Option> { - &self.super_ref - } - - /// Returns the constructor of the class. - pub(crate) fn constructor(&self) -> &Option { - &self.constructor - } - - /// Gets the list of all fields defined on the class. - pub(crate) fn elements(&self) -> &[ClassElement] { - &self.elements - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indent_n: usize, - ) -> String { - if self.elements.is_empty() && self.constructor().is_none() { - return format!( - "class {}{} {{}}", - interner.resolve_expect(self.name), - if let Some(node) = &self.super_ref { - format!(" extends {}", node.to_interned_string(interner)) - } else { - "".to_string() - } - ); - } - let indentation = " ".repeat(indent_n + 1); - let mut buf = format!( - "class {}{} {{\n", - interner.resolve_expect(self.name), - if let Some(node) = &self.super_ref { - format!("extends {}", node.to_interned_string(interner)) - } else { - "".to_string() - } - ); - if let Some(expr) = &self.constructor { - buf.push_str(&format!( - "{indentation}constructor({}) {}\n", - join_nodes(interner, &expr.parameters().parameters), - block_to_string(expr.body(), interner, indent_n + 1) - )); - } - for element in self.elements.iter() { - buf.push_str(&match element { - ClassElement::MethodDefinition(name, method) => { - format!( - "{indentation}{}{}({}) {}\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - name.to_interned_string(interner), - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Generator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::AsyncGenerator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Async(node) => { - join_nodes(interner, &node.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Async(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - }, - ) - } - ClassElement::StaticMethodDefinition(name, method) => { - format!( - "{indentation}static {}{}({}) {}\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - name.to_interned_string(interner), - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Generator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::AsyncGenerator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Async(node) => { - join_nodes(interner, &node.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Async(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - }, - ) - } - ClassElement::FieldDefinition(name, field) => match field { - Some(node) => { - format!( - "{indentation}{} = {};\n", - name.to_interned_string(interner), - node.to_no_indent_string(interner, indent_n + 1) - ) - } - None => { - format!("{indentation}{};\n", name.to_interned_string(interner),) - } - }, - ClassElement::StaticFieldDefinition(name, field) => match field { - Some(node) => { - format!( - "{indentation}static {} = {};\n", - name.to_interned_string(interner), - node.to_no_indent_string(interner, indent_n + 1) - ) - } - None => { - format!( - "{indentation}static {};\n", - name.to_interned_string(interner), - ) - } - }, - ClassElement::PrivateMethodDefinition(name, method) => { - format!( - "{indentation}{}#{}({}) {}\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - interner.resolve_expect(*name), - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Generator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::AsyncGenerator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Async(node) => { - join_nodes(interner, &node.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Async(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - }, - ) - } - ClassElement::PrivateStaticMethodDefinition(name, method) => { - format!( - "{indentation}static {}#{}({}) {}\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - interner.resolve_expect(*name), - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Generator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::AsyncGenerator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Async(node) => { - join_nodes(interner, &node.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Async(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - }, - ) - } - ClassElement::PrivateFieldDefinition(name, field) => match field { - Some(node) => { - format!( - "{indentation}#{} = {};\n", - interner.resolve_expect(*name), - node.to_no_indent_string(interner, indent_n + 1) - ) - } - None => { - format!("{indentation}#{};\n", interner.resolve_expect(*name),) - } - }, - ClassElement::PrivateStaticFieldDefinition(name, field) => match field { - Some(node) => { - format!( - "{indentation}static #{} = {};\n", - interner.resolve_expect(*name), - node.to_no_indent_string(interner, indent_n + 1) - ) - } - None => { - format!("{indentation}static #{};\n", interner.resolve_expect(*name),) - } - }, - ClassElement::StaticBlock(statement_list) => { - format!( - "{indentation}static {}\n", - block_to_string(statement_list, interner, indent_n + 1) - ) - } - }); - } - buf.push('}'); - buf - } -} - -impl ToInternedString for Class { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} - -/// Class element types. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum ClassElement { - MethodDefinition(PropertyName, MethodDefinition), - StaticMethodDefinition(PropertyName, MethodDefinition), - FieldDefinition(PropertyName, Option), - StaticFieldDefinition(PropertyName, Option), - PrivateMethodDefinition(Sym, MethodDefinition), - PrivateStaticMethodDefinition(Sym, MethodDefinition), - PrivateFieldDefinition(Sym, Option), - PrivateStaticFieldDefinition(Sym, Option), - StaticBlock(StatementList), -} diff --git a/boa_engine/src/syntax/ast/node/declaration/class_decl/tests.rs b/boa_engine/src/syntax/ast/node/declaration/class_decl/tests.rs deleted file mode 100644 index 5b5eb5af9e7..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/class_decl/tests.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::syntax::ast::node::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/node/declaration/function_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs deleted file mode 100644 index 3fdaf80c111..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `function` declaration (function statement) defines a function with the specified -/// parameters. -/// -/// A function created with a function declaration is a `Function` object and has all the -/// properties, methods and behavior of `Function`. -/// -/// A function can also be created using an expression (see [function expression][func_expr]). -/// -/// By default, functions return `undefined`. To return any other value, the function must have -/// a return statement that specifies the value to return. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function -/// [func_expr]: ../enum.Node.html#variant.FunctionExpr -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct FunctionDecl { - name: Sym, - parameters: FormalParameterList, - body: StatementList, -} - -impl FunctionDecl { - /// Creates a new function declaration. - pub(in crate::syntax) fn new(name: Sym, parameters: P, body: B) -> Self - where - P: Into, - B: Into, - { - Self { - name, - parameters: parameters.into(), - body: body.into(), - } - } - - /// Gets the name of the function declaration. - pub fn name(&self) -> Sym { - self.name - } - - /// Gets the list of parameters of the function declaration. - pub fn parameters(&self) -> &FormalParameterList { - &self.parameters - } - - /// Gets the body of the function declaration. - pub fn body(&self) -> &StatementList { - &self.body - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = format!( - "function {}({}", - interner.resolve_expect(self.name), - join_nodes(interner, &self.parameters.parameters) - ); - if self.body().items().is_empty() { - buf.push_str(") {}"); - } else { - buf.push_str(&format!( - ") {{\n{}{}}}", - self.body.to_indented_string(interner, indentation + 1), - " ".repeat(indentation) - )); - } - - buf - } -} - -impl From for Node { - fn from(decl: FunctionDecl) -> Self { - Self::FunctionDecl(decl) - } -} - -impl ToInternedString for FunctionDecl { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs deleted file mode 100644 index 3806f4b9d61..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -use super::block_to_string; - -/// The `function` expression defines a function with the specified parameters. -/// -/// A function created with a function expression is a `Function` object and has all the -/// properties, methods and behavior of `Function`. -/// -/// A function can also be created using a declaration (see function expression). -/// -/// By default, functions return `undefined`. To return any other value, the function must have -/// a return statement that specifies the value to return. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct FunctionExpr { - name: Option, - parameters: FormalParameterList, - body: StatementList, -} - -impl FunctionExpr { - /// Creates a new function expression - pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self - where - N: Into>, - P: Into, - B: Into, - { - Self { - name: name.into(), - parameters: parameters.into(), - body: body.into(), - } - } - - /// Gets the name of the function declaration. - pub fn name(&self) -> Option { - self.name - } - - /// Gets the list of parameters of the function declaration. - pub fn parameters(&self) -> &FormalParameterList { - &self.parameters - } - - /// Gets the body of the function declaration. - pub fn body(&self) -> &StatementList { - &self.body - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = "function".to_owned(); - if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name))); - } - buf.push_str(&format!( - "({}) {}", - join_nodes(interner, &self.parameters.parameters), - block_to_string(&self.body, interner, indentation) - )); - - buf - } -} - -impl ToInternedString for FunctionExpr { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} - -impl From for Node { - fn from(expr: FunctionExpr) -> Self { - Self::FunctionExpr(expr) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs deleted file mode 100644 index 49cee7955f4..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::syntax::ast::node::{join_nodes, FormalParameterList, Node, StatementList}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `function*` declaration (`function` keyword followed by an asterisk) defines a generator function, -/// which returns a `Generator` object. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct GeneratorDecl { - name: Sym, - parameters: FormalParameterList, - body: StatementList, -} - -impl GeneratorDecl { - /// Creates a new generator declaration. - pub(in crate::syntax) fn new(name: Sym, parameters: P, body: B) -> Self - where - P: Into, - B: Into, - { - Self { - name, - parameters: parameters.into(), - body: body.into(), - } - } - - /// Gets the name of the generator declaration. - pub fn name(&self) -> Sym { - self.name - } - - /// Gets the list of parameters of the generator declaration. - pub fn parameters(&self) -> &FormalParameterList { - &self.parameters - } - - /// Gets the body of the generator declaration. - pub fn body(&self) -> &StatementList { - &self.body - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = format!( - "function* {}({}", - interner.resolve_expect(self.name), - join_nodes(interner, &self.parameters.parameters) - ); - if self.body().items().is_empty() { - buf.push_str(") {}"); - } else { - buf.push_str(&format!( - ") {{\n{}{}}}", - self.body.to_indented_string(interner, indentation + 1), - " ".repeat(indentation) - )); - } - - buf - } -} - -impl From for Node { - fn from(decl: GeneratorDecl) -> Self { - Self::GeneratorDecl(decl) - } -} - -impl ToInternedString for GeneratorDecl { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/mod.rs b/boa_engine/src/syntax/ast/node/declaration/mod.rs deleted file mode 100644 index 73061c22b5b..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/mod.rs +++ /dev/null @@ -1,1114 +0,0 @@ -//! Declaration nodes -use crate::syntax::ast::node::{ - field::{GetConstField, GetField}, - join_nodes, - object::PropertyName, - statement_list::StatementList, - ContainsSymbol, Identifier, Node, -}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -pub mod arrow_function_decl; -pub mod async_function_decl; -pub mod async_function_expr; -pub mod async_generator_decl; -pub mod async_generator_expr; -pub mod class_decl; -pub mod function_decl; -pub mod function_expr; -pub mod generator_decl; -pub mod generator_expr; - -pub use self::{ - arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl, - async_function_expr::AsyncFunctionExpr, async_generator_decl::AsyncGeneratorDecl, - async_generator_expr::AsyncGeneratorExpr, function_decl::FunctionDecl, - function_expr::FunctionExpr, -}; - -#[cfg(test)] -mod tests; - -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum DeclarationList { - /// The `const` statements are block-scoped, much like variables defined using the `let` - /// keyword. - /// - /// This declaration creates a constant whose scope can be either global or local to the block - /// in which it is declared. Global constants do not become properties of the window object, - /// unlike var variables. - /// - /// An initializer for a constant is required. You must specify its value in the same statement - /// in which it's declared. (This makes sense, given that it can't be changed later.) - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const - /// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier - /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions - Const(Box<[Declaration]>), - - /// The `let` statement declares a block scope local variable, optionally initializing it to a - /// value. - /// - /// - /// `let` allows you to declare variables that are limited to a scope of a block statement, or - /// expression on which it is used, unlike the `var` keyword, which defines a variable - /// globally, or locally to an entire function regardless of block scope. - /// - /// Just like const the `let` does not create properties of the window object when declared - /// globally (in the top-most scope). - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let - Let(Box<[Declaration]>), - - /// The `var` statement declares a variable, optionally initializing it to a value. - /// - /// var declarations, wherever they occur, are processed before any code is executed. This is - /// called hoisting, and is discussed further below. - /// - /// The scope of a variable declared with var is its current execution context, which is either - /// the enclosing function or, for variables declared outside any function, global. If you - /// re-declare a JavaScript variable, it will not lose its value. - /// - /// Assigning a value to an undeclared variable implicitly creates it as a global variable (it - /// becomes a property of the global object) when the assignment is executed. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-VariableStatement - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var - Var(Box<[Declaration]>), -} - -impl AsRef<[Declaration]> for DeclarationList { - fn as_ref(&self) -> &[Declaration] { - use DeclarationList::{Const, Let, Var}; - match self { - Var(list) | Const(list) | Let(list) => list, - } - } -} - -impl ToInternedString for DeclarationList { - fn to_interned_string(&self, interner: &Interner) -> String { - if self.as_ref().is_empty() { - String::new() - } else { - use DeclarationList::{Const, Let, Var}; - format!( - "{} {}", - match &self { - Let(_) => "let", - Const(_) => "const", - Var(_) => "var", - }, - join_nodes(interner, self.as_ref()) - ) - } - } -} - -impl From for Node { - fn from(list: DeclarationList) -> Self { - use DeclarationList::{Const, Let, Var}; - match &list { - Let(_) => Self::LetDeclList(list), - Const(_) => Self::ConstDeclList(list), - Var(_) => Self::VarDeclList(list), - } - } -} - -impl From for Box<[Declaration]> { - fn from(d: Declaration) -> Self { - Box::new([d]) - } -} - -/// Declaration represents either an individual binding or a binding pattern. -/// -/// For `let` and `const` declarations this type represents a [`LexicalBinding`][spec1] -/// -/// For `var` declarations this type represents a [`VariableDeclaration`][spec2] -/// -/// More information: -/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum Declaration { - Identifier { - ident: Identifier, - init: Option, - }, - Pattern(DeclarationPattern), -} - -impl ToInternedString for Declaration { - fn to_interned_string(&self, interner: &Interner) -> String { - match &self { - Self::Identifier { ident, init } => { - let mut buf = ident.to_interned_string(interner); - if let Some(ref init) = &init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - Self::Pattern(pattern) => pattern.to_interned_string(interner), - } - } -} - -impl Declaration { - /// Creates a new variable declaration with a `BindingIdentifier`. - #[inline] - pub(in crate::syntax) fn new_with_identifier(ident: N, init: I) -> Self - where - N: Into, - I: Into>, - { - Self::Identifier { - ident: ident.into(), - init: init.into(), - } - } - - /// Creates a new variable declaration with an `ObjectBindingPattern`. - #[inline] - pub(in crate::syntax) fn new_with_object_pattern( - bindings: Vec, - init: I, - ) -> Self - where - I: Into>, - { - Self::Pattern(DeclarationPattern::Object(DeclarationPatternObject::new( - bindings, - init.into(), - ))) - } - - /// Creates a new variable declaration with an `ArrayBindingPattern`. - #[inline] - pub(in crate::syntax) fn new_with_array_pattern( - bindings: Vec, - init: I, - ) -> Self - where - I: Into>, - { - Self::Pattern(DeclarationPattern::Array(DeclarationPatternArray::new( - bindings, - init.into(), - ))) - } - - /// Gets the initialization node for the declaration, if any. - #[inline] - pub(crate) fn init(&self) -> Option<&Node> { - match &self { - Self::Identifier { init, .. } => init.as_ref(), - Self::Pattern(pattern) => pattern.init(), - } - } - - /// Returns `true` if the node contains the given token. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - match self { - Self::Identifier { init, .. } => { - if let Some(node) = init { - if node.contains(symbol) { - return true; - } - } - } - Self::Pattern(pattern) => { - if pattern.contains(symbol) { - return true; - } - } - } - false - } -} - -/// `DeclarationPattern` represents an object or array binding pattern. -/// -/// This enum mostly wraps the functionality of the specific binding pattern types. -/// -/// More information: -/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `BindingPattern`][spec1] -/// -/// [spec1]: https://tc39.es/ecma262/#prod-BindingPattern -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum DeclarationPattern { - Object(DeclarationPatternObject), - Array(DeclarationPatternArray), -} - -impl ToInternedString for DeclarationPattern { - fn to_interned_string(&self, interner: &Interner) -> String { - match &self { - DeclarationPattern::Object(o) => o.to_interned_string(interner), - DeclarationPattern::Array(a) => a.to_interned_string(interner), - } - } -} - -impl DeclarationPattern { - /// Gets the list of identifiers declared by the binding pattern. - /// - /// A single binding pattern may declare 0 to n identifiers. - #[inline] - pub fn idents(&self) -> Vec { - match &self { - DeclarationPattern::Object(pattern) => pattern.idents(), - DeclarationPattern::Array(pattern) => pattern.idents(), - } - } - - /// Gets the initialization node for the binding pattern, if any. - #[inline] - pub fn init(&self) -> Option<&Node> { - match &self { - DeclarationPattern::Object(pattern) => pattern.init(), - DeclarationPattern::Array(pattern) => pattern.init(), - } - } - - /// Returns true if the node contains a identifier reference named 'arguments'. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments - #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - match self { - DeclarationPattern::Object(pattern) => { - if let Some(init) = pattern.init() { - if init.contains_arguments() { - return true; - } - } - for binding in pattern.bindings() { - match binding { - BindingPatternTypeObject::SingleName { - property_name, - default_init, - .. - } => { - if let PropertyName::Computed(node) = property_name { - if node.contains_arguments() { - return true; - } - } - if let Some(init) = default_init { - if init.contains_arguments() { - return true; - } - } - } - BindingPatternTypeObject::AssignmentRestProperty { - get_const_field, - .. - } => { - if get_const_field.obj().contains_arguments() { - return true; - } - } - BindingPatternTypeObject::BindingPattern { - ident, - pattern, - default_init, - } => { - if let PropertyName::Computed(node) = ident { - if node.contains_arguments() { - return true; - } - } - if pattern.contains_arguments() { - return true; - } - if let Some(init) = default_init { - if init.contains_arguments() { - return true; - } - } - } - _ => {} - } - } - } - DeclarationPattern::Array(pattern) => { - if let Some(init) = pattern.init() { - if init.contains_arguments() { - return true; - } - } - for binding in pattern.bindings() { - match binding { - BindingPatternTypeArray::SingleName { - default_init: Some(init), - .. - } => { - if init.contains_arguments() { - return true; - } - } - BindingPatternTypeArray::GetField { get_field } - | BindingPatternTypeArray::GetFieldRest { get_field } => { - if get_field.obj().contains_arguments() { - return true; - } - if get_field.field().contains_arguments() { - return true; - } - } - BindingPatternTypeArray::GetConstField { get_const_field } - | BindingPatternTypeArray::GetConstFieldRest { get_const_field } => { - if get_const_field.obj().contains_arguments() { - return true; - } - } - BindingPatternTypeArray::BindingPattern { pattern } - | BindingPatternTypeArray::BindingPatternRest { pattern } => { - if pattern.contains_arguments() { - return true; - } - } - _ => {} - } - } - } - } - false - } - - /// Returns `true` if the node contains the given token. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - match self { - DeclarationPattern::Object(object) => { - if let Some(node) = object.init() { - if node.contains(symbol) { - return true; - } - } - for binding in &object.bindings { - match binding { - BindingPatternTypeObject::SingleName { - default_init: Some(node), - .. - } => { - if node.contains(symbol) { - return true; - } - } - BindingPatternTypeObject::AssignmentRestProperty { - get_const_field, - .. - } => { - if get_const_field.obj().contains(symbol) { - return true; - } - } - BindingPatternTypeObject::BindingPattern { - pattern, - default_init, - .. - } => { - if let Some(node) = default_init { - if node.contains(symbol) { - return true; - } - } - if pattern.contains(symbol) { - return true; - } - } - _ => {} - } - } - } - DeclarationPattern::Array(array) => { - if let Some(node) = array.init() { - if node.contains(symbol) { - return true; - } - } - for binding in array.bindings() { - match binding { - BindingPatternTypeArray::SingleName { - default_init: Some(node), - .. - } => { - if node.contains(symbol) { - return true; - } - } - BindingPatternTypeArray::GetField { get_field } - | BindingPatternTypeArray::GetFieldRest { get_field } => { - if get_field.obj().contains(symbol) - || get_field.field().contains(symbol) - { - return true; - } - } - BindingPatternTypeArray::GetConstField { get_const_field } - | BindingPatternTypeArray::GetConstFieldRest { get_const_field } => { - if get_const_field.obj().contains(symbol) { - return true; - } - } - BindingPatternTypeArray::BindingPattern { pattern } - | BindingPatternTypeArray::BindingPatternRest { pattern } => { - if pattern.contains(symbol) { - return true; - } - } - _ => {} - } - } - } - } - false - } -} - -/// `DeclarationPatternObject` represents an object binding pattern. -/// -/// This struct holds a list of bindings, and an optional initializer for the binding pattern. -/// -/// More information: -/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ObjectBindingPattern`][spec1] -/// -/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct DeclarationPatternObject { - bindings: Vec, - pub(crate) init: Option, -} - -impl ToInternedString for DeclarationPatternObject { - fn to_interned_string(&self, interner: &Interner) -> String { - let mut buf = "{".to_owned(); - for (i, binding) in self.bindings.iter().enumerate() { - let binding = binding.to_interned_string(interner); - let str = if i == self.bindings.len() - 1 { - format!("{binding} ") - } else { - format!("{binding},") - }; - - buf.push_str(&str); - } - buf.push('}'); - if let Some(ref init) = self.init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } -} - -impl DeclarationPatternObject { - /// Create a new object binding pattern. - #[inline] - pub(in crate::syntax) fn new( - bindings: Vec, - init: Option, - ) -> Self { - Self { bindings, init } - } - - /// Gets the initialization node for the object binding pattern, if any. - #[inline] - pub(crate) fn init(&self) -> Option<&Node> { - self.init.as_ref() - } - - /// Gets the bindings for the object binding pattern. - #[inline] - pub(crate) fn bindings(&self) -> &Vec { - &self.bindings - } - - // Returns if the object binding pattern has a rest element. - #[inline] - pub(crate) fn has_rest(&self) -> bool { - matches!( - self.bindings.last(), - Some(BindingPatternTypeObject::RestProperty { .. }) - ) - } - - /// Gets the list of identifiers declared by the object binding pattern. - #[inline] - pub(crate) fn idents(&self) -> Vec { - let mut idents = Vec::new(); - - for binding in &self.bindings { - use BindingPatternTypeObject::{ - AssignmentRestProperty, BindingPattern, Empty, RestProperty, SingleName, - }; - - match binding { - Empty | AssignmentRestProperty { .. } => {} - SingleName { - ident, - property_name: _, - default_init: _, - } => { - idents.push(*ident); - } - RestProperty { - ident: property_name, - excluded_keys: _, - } => { - idents.push(*property_name); - } - BindingPatternTypeObject::AssignmentGetConstField { property_name, .. } - | BindingPatternTypeObject::AssignmentGetField { property_name, .. } => { - if let Some(name) = property_name.literal() { - idents.push(name); - } - } - BindingPattern { - ident: _, - pattern, - default_init: _, - } => { - for ident in pattern.idents() { - idents.push(ident); - } - } - } - } - - idents - } -} - -/// `DeclarationPatternArray` represents an array binding pattern. -/// -/// This struct holds a list of bindings, and an optional initializer for the binding pattern. -/// -/// More information: -/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ArrayBindingPattern`][spec1] -/// -/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct DeclarationPatternArray { - bindings: Vec, - pub(crate) init: Option, -} - -impl ToInternedString for DeclarationPatternArray { - fn to_interned_string(&self, interner: &Interner) -> String { - let mut buf = "[".to_owned(); - for (i, binding) in self.bindings.iter().enumerate() { - if i == self.bindings.len() - 1 { - match binding { - BindingPatternTypeArray::Elision => { - buf.push_str(&format!("{}, ", binding.to_interned_string(interner))); - } - _ => buf.push_str(&format!("{} ", binding.to_interned_string(interner))), - } - } else { - buf.push_str(&format!("{},", binding.to_interned_string(interner))); - } - } - buf.push(']'); - if let Some(ref init) = self.init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } -} - -impl DeclarationPatternArray { - /// Create a new array binding pattern. - #[inline] - pub(in crate::syntax) fn new( - bindings: Vec, - init: Option, - ) -> Self { - Self { bindings, init } - } - - /// Gets the initialization node for the array binding pattern, if any. - #[inline] - pub(crate) fn init(&self) -> Option<&Node> { - self.init.as_ref() - } - - /// Gets the bindings for the array binding pattern. - #[inline] - pub(crate) fn bindings(&self) -> &Vec { - &self.bindings - } - - /// Gets the list of identifiers declared by the array binding pattern. - #[inline] - pub(crate) fn idents(&self) -> Vec { - let mut idents = Vec::new(); - - for binding in &self.bindings { - use BindingPatternTypeArray::{ - BindingPattern, BindingPatternRest, Elision, Empty, GetConstField, - GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest, - }; - - match binding { - Empty - | Elision - | GetField { .. } - | GetConstField { .. } - | GetFieldRest { .. } - | GetConstFieldRest { .. } => {} - SingleName { - ident, - default_init: _, - } => { - idents.push(*ident); - } - BindingPattern { pattern } | BindingPatternRest { pattern } => { - let mut i = pattern.idents(); - idents.append(&mut i); - } - SingleNameRest { ident } => idents.push(*ident), - } - } - - idents - } -} - -/// `BindingPatternTypeObject` represents the different types of bindings that an object binding pattern may contain. -/// -/// More information: -/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ObjectBindingPattern`][spec1] -/// -/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum BindingPatternTypeObject { - /// Empty represents an empty object binding pattern e.g. `{ }`. - Empty, - - /// SingleName represents one of the following properties: - /// - /// - `SingleNameBinding` with an identifier and an optional default initializer. - /// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding - /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty - SingleName { - ident: Sym, - property_name: PropertyName, - default_init: Option, - }, - - /// RestProperty represents a `BindingRestProperty` with an identifier. - /// - /// It also includes a list of the property keys that should be excluded from the rest, - /// because they where already assigned. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty - RestProperty { ident: Sym, excluded_keys: Vec }, - - /// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget. - /// - /// Note: According to the spec this is not part of an ObjectBindingPattern. - /// This is only used when a object literal is used to cover an AssignmentPattern. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty - AssignmentRestProperty { - get_const_field: GetConstField, - excluded_keys: Vec, - }, - - /// AssignmentGetConstField represents an AssignmentProperty with a cost field member expression AssignmentElement. - /// - /// Note: According to the spec this is not part of an ObjectBindingPattern. - /// This is only used when a object literal is used to cover an AssignmentPattern. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty - AssignmentGetConstField { - property_name: PropertyName, - get_const_field: GetConstField, - default_init: Option, - }, - - /// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. - /// - /// Note: According to the spec this is not part of an ObjectBindingPattern. - /// This is only used when a object literal is used to cover an AssignmentPattern. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty - AssignmentGetField { - property_name: PropertyName, - get_field: GetField, - default_init: Option, - }, - - /// BindingPattern represents a `BindingProperty` with a `BindingPattern` as the `BindingElement`. - /// - /// Additionally to the identifier of the new property and the nested binding pattern, - /// this may also include an optional default initializer. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty - BindingPattern { - ident: PropertyName, - pattern: DeclarationPattern, - default_init: Option, - }, -} - -impl ToInternedString for BindingPatternTypeObject { - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - Self::Empty => String::new(), - Self::SingleName { - ident, - property_name, - default_init, - } => { - let mut buf = match property_name { - PropertyName::Literal(name) if *name == *ident => { - format!(" {}", interner.resolve_expect(*ident)) - } - PropertyName::Literal(name) => { - format!( - " {} : {}", - interner.resolve_expect(*name), - interner.resolve_expect(*ident) - ) - } - PropertyName::Computed(node) => { - format!( - " [{}] : {}", - node.to_interned_string(interner), - interner.resolve_expect(*ident) - ) - } - }; - if let Some(ref init) = default_init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - Self::RestProperty { - ident: property_name, - excluded_keys: _, - } => { - format!(" ... {}", interner.resolve_expect(*property_name)) - } - Self::AssignmentRestProperty { - get_const_field, .. - } => { - format!(" ... {}", get_const_field.to_interned_string(interner)) - } - Self::AssignmentGetConstField { - property_name, - get_const_field, - default_init, - } => { - let mut buf = match property_name { - PropertyName::Literal(name) => { - format!( - " {} : {}", - interner.resolve_expect(*name), - get_const_field.to_interned_string(interner) - ) - } - PropertyName::Computed(node) => { - format!( - " [{}] : {}", - node.to_interned_string(interner), - get_const_field.to_interned_string(interner) - ) - } - }; - if let Some(init) = &default_init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - Self::AssignmentGetField { - property_name, - get_field, - default_init, - } => { - let mut buf = match property_name { - PropertyName::Literal(name) => { - format!( - " {} : {}", - interner.resolve_expect(*name), - get_field.to_interned_string(interner) - ) - } - PropertyName::Computed(node) => { - format!( - " [{}] : {}", - node.to_interned_string(interner), - get_field.to_interned_string(interner) - ) - } - }; - if let Some(init) = &default_init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - Self::BindingPattern { - ident: property_name, - pattern, - default_init, - } => { - let mut buf = match property_name { - PropertyName::Literal(name) => { - format!( - " {} : {}", - interner.resolve_expect(*name), - pattern.to_interned_string(interner), - ) - } - PropertyName::Computed(node) => { - format!( - " [{}] : {}", - node.to_interned_string(interner), - pattern.to_interned_string(interner), - ) - } - }; - if let Some(ref init) = default_init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - } - } -} - -/// `BindingPatternTypeArray` represents the different types of bindings that an array binding pattern may contain. -/// -/// More information: -/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ArrayBindingPattern`][spec1] -/// -/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum BindingPatternTypeArray { - /// Empty represents an empty array binding pattern e.g. `[ ]`. - /// - /// This may occur because the `Elision` and `BindingRestElement` in the first type of - /// array binding pattern are both optional. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern - Empty, - - /// Elision represents the elision of an item in the array binding pattern. - /// - /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions. - /// This variant strictly represents one elision. If there are multiple, this should be used multiple times. - /// - /// More information: - /// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-Elision - Elision, - - /// SingleName represents a `SingleNameBinding` with an identifier and an optional default initializer. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding - SingleName { - ident: Sym, - default_init: Option, - }, - - /// GetField represents a binding with a property accessor. - /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. - /// This is only used when a array literal is used as the left-hand-side of an assignment expression. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - GetField { get_field: GetField }, - - /// GetConstField represents a binding with a property accessor. - /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. - /// This is only used when a array literal is used as the left-hand-side of an assignment expression. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - GetConstField { get_const_field: GetConstField }, - - /// BindingPattern represents a `BindingPattern` in a `BindingElement` of an array binding pattern. - /// - /// The pattern and the optional default initializer are both stored in the DeclarationPattern. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement - BindingPattern { pattern: DeclarationPattern }, - - /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array binding pattern. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement - SingleNameRest { ident: Sym }, - - /// GetFieldRest represents a rest binding (spread operator) with a property accessor. - /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. - /// This is only used when a array literal is used as the left-hand-side of an assignment expression. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - GetFieldRest { get_field: GetField }, - - /// GetConstFieldRest represents a rest binding (spread operator) with a property accessor. - /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. - /// This is only used when a array literal is used as the left-hand-side of an assignment expression. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - GetConstFieldRest { get_const_field: GetConstField }, - - /// SingleNameRest represents a `BindingPattern` in a `BindingRestElement` of an array binding pattern. - /// - /// More information: - /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] - /// - /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement - BindingPatternRest { pattern: DeclarationPattern }, -} - -impl ToInternedString for BindingPatternTypeArray { - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - Self::Empty => String::new(), - Self::Elision => " ".to_owned(), - Self::SingleName { - ident, - default_init, - } => { - let mut buf = format!(" {}", interner.resolve_expect(*ident)); - if let Some(ref init) = default_init { - buf.push_str(&format!(" = {}", init.to_interned_string(interner))); - } - buf - } - Self::GetField { get_field } => { - format!(" {}", get_field.to_interned_string(interner)) - } - Self::GetConstField { get_const_field } => { - format!(" {}", get_const_field.to_interned_string(interner)) - } - Self::BindingPattern { pattern } => { - format!(" {}", pattern.to_interned_string(interner)) - } - Self::SingleNameRest { ident } => { - format!(" ... {}", interner.resolve_expect(*ident)) - } - Self::GetFieldRest { get_field } => { - format!(" ... {}", get_field.to_interned_string(interner)) - } - Self::GetConstFieldRest { get_const_field } => { - format!(" ... {}", get_const_field.to_interned_string(interner)) - } - Self::BindingPatternRest { pattern } => { - format!(" ... {}", pattern.to_interned_string(interner)) - } - } - } -} - -/// Displays the body of a block or statement list. -/// -/// This includes the curly braces at the start and end. This will not indent the first brace, -/// but will indent the last brace. -pub(in crate::syntax::ast::node) fn block_to_string( - body: &StatementList, - interner: &Interner, - indentation: usize, -) -> String { - if body.items().is_empty() { - "{}".to_owned() - } else { - format!( - "{{\n{}{}}}", - body.to_indented_string(interner, indentation + 1), - " ".repeat(indentation) - ) - } -} diff --git a/boa_engine/src/syntax/ast/node/declaration/tests.rs b/boa_engine/src/syntax/ast/node/declaration/tests.rs deleted file mode 100644 index d45f2797d76..00000000000 --- a/boa_engine/src/syntax/ast/node/declaration/tests.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::exec; - -#[test] -fn duplicate_function_name() { - let scenario = r#" - function f () {} - function f () {return 12;} - f() - "#; - - assert_eq!(&exec(scenario), "12"); -} - -#[test] -fn fmt() { - super::super::test_formatting( - r#" - function func(a, b) { - console.log(a); - }; - function func_2(a, b) {}; - let arrow_func = (a, b) => { - console.log("in multi statement arrow"); - console.log(b); - }; - async function async_func(a, b) { - console.log(a); - }; - pass_async_func(async function(a, b) { - console.log("in async callback", a); - }); - pass_func(function(a, b) { - console.log("in callback", a); - }); - let arrow_func_2 = (a, b) => {}; - async function async_func_2(a, b) {}; - pass_async_func(async function(a, b) {}); - pass_func(function(a, b) {}); - "#, - ); -} - -#[test] -fn fmt_binding_pattern() { - super::super::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/node/field/get_const_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs deleted file mode 100644 index b9f1501559e..00000000000 --- a/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// This property accessor provides access to an object's properties by using the -/// [dot notation][mdn]. -/// -/// In the object.property syntax, the property must be a valid JavaScript identifier. -/// (In the ECMAScript standard, the names of properties are technically `IdentifierNames`, not -/// "Identifiers", so reserved words can be used but are not recommended). -/// -/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup -/// table). The keys in this array are the names of the object's properties. -/// -/// It's typical when speaking of an object's properties to make a distinction between -/// properties and methods. However, the property/method distinction is little more than a -/// convention. A method is simply a property that can be called (for example, if it has a -/// reference to a Function instance as its value). -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-property-accessors -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Dot_notation -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct GetConstField { - obj: Box, - field: Sym, -} - -impl GetConstField { - /// Creates a `GetConstField` AST node. - pub fn new(value: V, field: Sym) -> Self - where - V: Into, - { - Self { - obj: Box::new(value.into()), - field, - } - } - - /// Gets the original object from where to get the field from. - pub fn obj(&self) -> &Node { - &self.obj - } - - /// Gets the name of the field to retrieve. - pub fn field(&self) -> Sym { - self.field - } -} - -impl ToInternedString for GetConstField { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{}.{}", - self.obj.to_interned_string(interner), - interner.resolve_expect(self.field) - ) - } -} - -impl From for Node { - fn from(get_const_field: GetConstField) -> Self { - Self::GetConstField(get_const_field) - } -} diff --git a/boa_engine/src/syntax/ast/node/field/get_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_field/mod.rs deleted file mode 100644 index 7b69a3492d3..00000000000 --- a/boa_engine/src/syntax/ast/node/field/get_field/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// This property accessor provides access to an object's properties by using the -/// [bracket notation][mdn]. -/// -/// In the `object[property_name]` syntax, the `property_name` is just a string or -/// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a -/// space). -/// -/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup -/// table). The keys in this array are the names of the object's properties. -/// -/// It's typical when speaking of an object's properties to make a distinction between -/// properties and methods. However, the property/method distinction is little more than a -/// convention. A method is simply a property that can be called (for example, if it has a -/// reference to a Function instance as its value). -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#sec-property-accessors -/// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct GetField { - obj: Box, - field: Box, -} - -impl GetField { - pub fn obj(&self) -> &Node { - &self.obj - } - - pub fn field(&self) -> &Node { - &self.field - } - - /// Creates a `GetField` AST node. - pub fn new(value: V, field: F) -> Self - where - V: Into, - F: Into, - { - Self { - obj: Box::new(value.into()), - field: Box::new(field.into()), - } - } -} - -impl ToInternedString for GetField { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{}[{}]", - self.obj.to_interned_string(interner), - self.field.to_interned_string(interner) - ) - } -} - -impl From for Node { - fn from(get_field: GetField) -> Self { - Self::GetField(get_field) - } -} diff --git a/boa_engine/src/syntax/ast/node/field/get_private_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_private_field/mod.rs deleted file mode 100644 index 3540967d664..00000000000 --- a/boa_engine/src/syntax/ast/node/field/get_private_field/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// This property accessor provides access to an class object's private fields. -/// -/// This expression can be described as ` MemberExpression.PrivateIdentifier` -/// Example: `this.#a` -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct GetPrivateField { - obj: Box, - field: Sym, -} - -impl GetPrivateField { - /// Creates a `GetPrivateField` AST node. - pub fn new(value: V, field: Sym) -> Self - where - V: Into, - { - Self { - obj: Box::new(value.into()), - field, - } - } - - /// Gets the original object from where to get the field from. - pub fn obj(&self) -> &Node { - &self.obj - } - - /// Gets the name of the field to retrieve. - pub fn field(&self) -> Sym { - self.field - } -} - -impl ToInternedString for GetPrivateField { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{}.#{}", - self.obj.to_interned_string(interner), - interner.resolve_expect(self.field) - ) - } -} - -impl From for Node { - fn from(get_private_field: GetPrivateField) -> Self { - Self::GetPrivateField(get_private_field) - } -} diff --git a/boa_engine/src/syntax/ast/node/field/get_super_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_super_field/mod.rs deleted file mode 100644 index 2d134e1dcb2..00000000000 --- a/boa_engine/src/syntax/ast/node/field/get_super_field/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `super` keyword is used to access fields on an object's parent. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum GetSuperField { - Const(Sym), - Expr(Box), -} - -impl From for GetSuperField { - fn from(field: Sym) -> Self { - Self::Const(field) - } -} - -impl From for GetSuperField { - fn from(field: Node) -> Self { - Self::Expr(Box::new(field)) - } -} - -impl ToInternedString for GetSuperField { - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - GetSuperField::Const(field) => format!("super.{}", interner.resolve_expect(*field)), - GetSuperField::Expr(field) => format!("super[{}]", field.to_interned_string(interner)), - } - } -} - -impl From for Node { - fn from(get_super_field: GetSuperField) -> Self { - Self::GetSuperField(get_super_field) - } -} diff --git a/boa_engine/src/syntax/ast/node/field/mod.rs b/boa_engine/src/syntax/ast/node/field/mod.rs deleted file mode 100644 index b6da8ed7759..00000000000 --- a/boa_engine/src/syntax/ast/node/field/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Field nodes - -pub mod get_const_field; -pub mod get_field; -pub mod get_private_field; -pub mod get_super_field; - -pub use self::{ - get_const_field::GetConstField, get_field::GetField, get_private_field::GetPrivateField, - get_super_field::GetSuperField, -}; - -#[cfg(test)] -mod tests; diff --git a/boa_engine/src/syntax/ast/node/field/tests.rs b/boa_engine/src/syntax/ast/node/field/tests.rs deleted file mode 100644 index 3c4cb69ce91..00000000000 --- a/boa_engine/src/syntax/ast/node/field/tests.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - a.field_name; - a[5]; - a["other_field_name"]; - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/iteration/break_node/tests.rs b/boa_engine/src/syntax/ast/node/iteration/break_node/tests.rs deleted file mode 100644 index ac9335d95f5..00000000000 --- a/boa_engine/src/syntax/ast/node/iteration/break_node/tests.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::syntax::ast::node::test_formatting; - -#[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(); - // } - // ``` - test_formatting( - r#" - { - while (true) { - break outer; - } - skipped_call(); - } - while (true) { - break; - } - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs deleted file mode 100644 index 2094c106694..00000000000 --- a/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `for` statement creates a loop that consists of three optional expressions. -/// -/// A `for` loop repeats until a specified condition evaluates to `false`. -/// The JavaScript for loop is similar to the Java and C for loop. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct ForLoop { - #[cfg_attr(feature = "deser", serde(flatten))] - inner: Box, - label: Option, -} - -impl ForLoop { - /// Creates a new for loop AST node. - pub(in crate::syntax) fn new(init: I, condition: C, final_expr: E, body: B) -> Self - where - I: Into>, - C: Into>, - E: Into>, - B: Into, - { - Self { - inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)), - label: None, - } - } - - /// Gets the initialization node. - pub fn init(&self) -> Option<&Node> { - self.inner.init() - } - - /// Gets the loop condition node. - pub fn condition(&self) -> Option<&Node> { - self.inner.condition() - } - - /// Gets the final expression node. - pub fn final_expr(&self) -> Option<&Node> { - self.inner.final_expr() - } - - /// Gets the body of the for loop. - pub fn body(&self) -> &Node { - self.inner.body() - } - - /// Converts the for loop to a string with the given indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { - let mut buf = if let Some(label) = self.label { - format!("{}: ", interner.resolve_expect(label)) - } else { - String::new() - }; - buf.push_str("for ("); - if let Some(init) = self.init() { - buf.push_str(&init.to_interned_string(interner)); - } - buf.push_str("; "); - if let Some(condition) = self.condition() { - buf.push_str(&condition.to_interned_string(interner)); - } - buf.push_str("; "); - if let Some(final_expr) = self.final_expr() { - buf.push_str(&final_expr.to_interned_string(interner)); - } - buf.push_str(&format!( - ") {}", - self.inner.body().to_indented_string(interner, indentation) - )); - - buf - } - - pub fn label(&self) -> Option { - self.label - } - - pub fn set_label(&mut self, label: Sym) { - self.label = Some(label); - } -} - -impl ToInternedString for ForLoop { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} - -impl From for Node { - fn from(for_loop: ForLoop) -> Self { - Self::ForLoop(for_loop) - } -} - -/// Inner structure to avoid multiple indirections in the heap. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -struct InnerForLoop { - init: Option, - condition: Option, - final_expr: Option, - body: Node, -} - -impl InnerForLoop { - /// Creates a new inner for loop. - fn new(init: I, condition: C, final_expr: E, body: B) -> Self - where - I: Into>, - C: Into>, - E: Into>, - B: Into, - { - Self { - init: init.into(), - condition: condition.into(), - final_expr: final_expr.into(), - body: body.into(), - } - } - - /// Gets the initialization node. - fn init(&self) -> Option<&Node> { - self.init.as_ref() - } - - /// Gets the loop condition node. - fn condition(&self) -> Option<&Node> { - self.condition.as_ref() - } - - /// Gets the final expression node. - fn final_expr(&self) -> Option<&Node> { - self.final_expr.as_ref() - } - - /// Gets the body of the for loop. - fn body(&self) -> &Node { - &self.body - } -} diff --git a/boa_engine/src/syntax/ast/node/iteration/mod.rs b/boa_engine/src/syntax/ast/node/iteration/mod.rs deleted file mode 100644 index 1dabe8f9b45..00000000000 --- a/boa_engine/src/syntax/ast/node/iteration/mod.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Iteration nodes - -pub use self::{ - break_node::Break, continue_node::Continue, do_while_loop::DoWhileLoop, for_in_loop::ForInLoop, - for_loop::ForLoop, for_of_loop::ForOfLoop, while_loop::WhileLoop, -}; -use crate::syntax::ast::node::{ - declaration::Declaration, identifier::Identifier, DeclarationPattern, -}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum IterableLoopInitializer { - Identifier(Identifier), - Var(Declaration), - Let(Declaration), - Const(Declaration), - DeclarationPattern(DeclarationPattern), -} - -impl IterableLoopInitializer { - /// Return the bound names of a for loop initializer. - /// - /// The returned list may contain duplicates. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames - pub(crate) fn bound_names(&self) -> Vec { - match self { - IterableLoopInitializer::Let(decl) | IterableLoopInitializer::Const(decl) => match decl - { - Declaration::Identifier { ident, .. } => vec![ident.sym()], - Declaration::Pattern(pattern) => pattern.idents(), - }, - _ => Vec::new(), - } - } -} - -impl ToInternedString for IterableLoopInitializer { - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - IterableLoopInitializer::Identifier(identifier) => { - identifier.to_interned_string(interner) - } - IterableLoopInitializer::Var(declaration) => { - format!("var {}", declaration.to_interned_string(interner)) - } - IterableLoopInitializer::Let(declaration) => { - format!("let {}", declaration.to_interned_string(interner)) - } - IterableLoopInitializer::Const(declaration) => { - format!("const {}", declaration.to_interned_string(interner)) - } - IterableLoopInitializer::DeclarationPattern(declaration) => { - declaration.to_interned_string(interner) - } - } - } -} - -pub mod break_node; -pub mod continue_node; -pub mod do_while_loop; -pub mod for_in_loop; -pub mod for_loop; -pub mod for_of_loop; -pub mod while_loop; diff --git a/boa_engine/src/syntax/ast/node/mod.rs b/boa_engine/src/syntax/ast/node/mod.rs deleted file mode 100644 index f5648801711..00000000000 --- a/boa_engine/src/syntax/ast/node/mod.rs +++ /dev/null @@ -1,1376 +0,0 @@ -//! This module implements the `Node` structure, which composes the AST. - -mod parameters; - -pub mod array; -pub mod await_expr; -pub mod block; -pub mod call; -pub mod conditional; -pub mod declaration; -pub mod field; -pub mod identifier; -pub mod iteration; -pub mod new; -pub mod object; -pub mod operator; -pub mod return_smt; -pub mod spread; -pub mod statement_list; -pub mod super_call; -pub mod switch; -pub mod template; -pub mod throw; -pub mod try_node; -pub mod r#yield; - -pub use self::{ - array::ArrayDecl, - await_expr::AwaitExpr, - block::Block, - call::Call, - conditional::{ConditionalOp, If}, - declaration::{ - async_generator_decl::AsyncGeneratorDecl, async_generator_expr::AsyncGeneratorExpr, - class_decl::Class, generator_decl::GeneratorDecl, generator_expr::GeneratorExpr, - ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList, - DeclarationPattern, FunctionDecl, FunctionExpr, - }, - field::{get_private_field::GetPrivateField, GetConstField, GetField, GetSuperField}, - identifier::Identifier, - iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop}, - new::New, - object::Object, - operator::{Assign, BinOp, UnaryOp}, - parameters::{FormalParameter, FormalParameterList}, - r#yield::Yield, - return_smt::Return, - spread::Spread, - statement_list::StatementList, - super_call::SuperCall, - switch::{Case, Switch}, - template::{TaggedTemplate, TemplateLit}, - throw::Throw, - try_node::{Catch, Finally, Try}, -}; -use self::{ - declaration::class_decl::ClassElement, - iteration::IterableLoopInitializer, - object::{MethodDefinition, PropertyDefinition}, - operator::assign::AssignTarget, -}; - -pub(crate) use self::parameters::FormalParameterListFlags; - -use super::Const; -use boa_interner::{Interner, Sym, ToInternedString}; -use rustc_hash::FxHashSet; -use std::cmp::Ordering; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -// TODO: This should be split into Expression and Statement. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum Node { - /// Array declaration node. [More information](./array/struct.ArrayDecl.html). - ArrayDecl(ArrayDecl), - - /// An arrow function expression node. [More information](./arrow_function/struct.ArrowFunctionDecl.html). - ArrowFunctionDecl(ArrowFunctionDecl), - - /// An assignment operator node. [More information](./operator/struct.Assign.html). - Assign(Assign), - - /// An async function declaration node. [More information](./declaration/struct.AsyncFunctionDecl.html). - AsyncFunctionDecl(AsyncFunctionDecl), - - /// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html). - AsyncFunctionExpr(AsyncFunctionExpr), - - /// An async generator expression node. - AsyncGeneratorExpr(AsyncGeneratorExpr), - - /// An async generator declaration node. - AsyncGeneratorDecl(AsyncGeneratorDecl), - - /// An await expression node. [More information](./await_expr/struct.AwaitExpression.html). - AwaitExpr(AwaitExpr), - - /// A binary operator node. [More information](./operator/struct.BinOp.html). - BinOp(BinOp), - - /// A Block node. [More information](./block/struct.Block.html). - Block(Block), - - /// A break node. [More information](./break/struct.Break.html). - Break(Break), - - /// A function call. [More information](./expression/struct.Call.html). - Call(Call), - - /// A javascript conditional operand ( x ? y : z ). [More information](./conditional/struct.ConditionalOp.html). - ConditionalOp(ConditionalOp), - - /// Literals represent values in JavaScript. - /// - /// These are fixed values not variables that you literally provide in your script. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals - Const(Const), - - /// A constant declaration list. [More information](./declaration/enum.DeclarationList.html#variant.Const). - ConstDeclList(DeclarationList), - - /// A continue statement. [More information](./iteration/struct.Continue.html). - Continue(Continue), - - /// A do ... while statement. [More information](./iteration/struct.DoWhileLoop.html). - DoWhileLoop(DoWhileLoop), - - /// A function declaration node. [More information](./declaration/struct.FunctionDecl.html). - FunctionDecl(FunctionDecl), - - /// A function expression node. [More information](./declaration/struct.FunctionExpr.html). - FunctionExpr(FunctionExpr), - - /// Provides access to an object types' constant properties. [More information](./declaration/struct.GetConstField.html). - GetConstField(GetConstField), - - /// Provides access to an object types' private properties. [More information](./declaration/struct.GetPrivateField.html). - GetPrivateField(GetPrivateField), - - /// Provides access to object fields. [More information](./declaration/struct.GetField.html). - GetField(GetField), - - /// Provides access to super fields. [More information](./declaration/struct.GetSuperField.html). - GetSuperField(GetSuperField), - - /// A `for` statement. [More information](./iteration/struct.ForLoop.html). - ForLoop(ForLoop), - - /// A `for...of` or `for..in` statement. [More information](./iteration/struct.ForIn.html). - ForInLoop(ForInLoop), - - /// A `for...of` statement. [More information](./iteration/struct.ForOf.html). - ForOfLoop(ForOfLoop), - - /// An 'if' statement. [More information](./conditional/struct.If.html). - If(If), - - /// A `let` declaration list. [More information](./declaration/enum.DeclarationList.html#variant.Let). - LetDeclList(DeclarationList), - - /// A local identifier node. [More information](./identifier/struct.Identifier.html). - Identifier(Identifier), - - /// A `new` expression. [More information](./expression/struct.New.html). - New(New), - - /// An object. [More information](./object/struct.Object.html). - Object(Object), - - /// A return statement. [More information](./object/struct.Return.html). - Return(Return), - - /// A switch {case} statement. [More information](./switch/struct.Switch.html). - Switch(Switch), - - /// A spread (...x) statement. [More information](./spread/struct.Spread.html). - Spread(Spread), - - /// A tagged template. [More information](./template/struct.TaggedTemplate.html). - TaggedTemplate(Box), - - /// A template literal. [More information](./template/struct.TemplateLit.html). - TemplateLit(TemplateLit), - - /// A throw statement. [More information](./throw/struct.Throw.html). - Throw(Throw), - - /// A `try...catch` node. [More information](./try_node/struct.Try.htl). - Try(Box), - - /// The JavaScript `this` keyword refers to the object it belongs to. - /// - /// A property of an execution context (global, function or eval) that, - /// in non–strict mode, is always a reference to an object and in strict - /// mode can be any value. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-this-keyword - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this - This, - - /// Unary operation node. [More information](./operator/struct.UnaryOp.html) - UnaryOp(UnaryOp), - - /// Array declaration node. [More information](./declaration/enum.DeclarationList.html#variant.Var). - VarDeclList(DeclarationList), - - /// A 'while {...}' node. [More information](./iteration/struct.WhileLoop.html). - WhileLoop(WhileLoop), - - /// A empty node. - /// - /// Empty statement do nothing, just return undefined. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty - Empty, - - /// A `yield` node. [More information](./yield/struct.Yield.html). - Yield(Yield), - - /// A generator function declaration node. [More information](./declaration/struct.GeneratorDecl.html). - GeneratorDecl(GeneratorDecl), - - /// A generator function expression node. [More information](./declaration/struct.GeneratorExpr.html). - GeneratorExpr(GeneratorExpr), - - /// A class declaration. [More information](./declaration/struct.class_decl.Class.html). - ClassDecl(Class), - - /// A class declaration. [More information](./declaration/struct.class_decl.Class.html). - ClassExpr(Class), - - /// A call of the super constructor. [More information](./super_call/struct.SuperCall.html). - SuperCall(SuperCall), - - /// The `new.target` pseudo-property expression. - NewTarget, - - /// A FormalParameterList. - /// - /// This is only used in the parser itself. - /// It is not a valid AST node. - #[doc(hidden)] - FormalParameterList(FormalParameterList), -} - -impl From for Node { - fn from(c: Const) -> Self { - Self::Const(c) - } -} - -impl Node { - /// Returns a node ordering based on the hoistability of each node. - pub(crate) fn hoistable_order(a: &Self, b: &Self) -> Ordering { - match (a, b) { - (Node::FunctionDecl(_), Node::FunctionDecl(_)) => Ordering::Equal, - (_, Node::FunctionDecl(_)) => Ordering::Greater, - (Node::FunctionDecl(_), _) => Ordering::Less, - - (_, _) => Ordering::Equal, - } - } - - /// Creates a `This` AST node. - pub fn this() -> Self { - Self::This - } - - /// Creates a string of the value of the node with the given indentation. For example, an - /// indent level of 2 would produce this: - /// - /// ```js - /// function hello() { - /// console.log("hello"); - /// } - /// hello(); - /// a = 2; - /// ``` - fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { - let mut buf = match *self { - Self::Block(_) => String::new(), - _ => " ".repeat(indentation), - }; - - buf.push_str(&self.to_no_indent_string(interner, indentation)); - - buf - } - - /// Implements the display formatting with indentation. - /// - /// 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). - fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { - match *self { - Self::Call(ref expr) => expr.to_interned_string(interner), - Self::Const(ref c) => c.to_interned_string(interner), - Self::ConditionalOp(ref cond_op) => cond_op.to_interned_string(interner), - Self::ForLoop(ref for_loop) => for_loop.to_indented_string(interner, indentation), - Self::ForOfLoop(ref for_of) => for_of.to_indented_string(interner, indentation), - Self::ForInLoop(ref for_in) => for_in.to_indented_string(interner, indentation), - Self::This => "this".to_owned(), - Self::Try(ref try_catch) => try_catch.to_indented_string(interner, indentation), - Self::Break(ref break_smt) => break_smt.to_interned_string(interner), - Self::Continue(ref cont) => cont.to_interned_string(interner), - Self::Spread(ref spread) => spread.to_interned_string(interner), - Self::Block(ref block) => block.to_indented_string(interner, indentation), - Self::Identifier(ref ident) => ident.to_interned_string(interner), - Self::New(ref expr) => expr.to_interned_string(interner), - Self::GetConstField(ref get_const_field) => { - get_const_field.to_interned_string(interner) - } - Self::GetPrivateField(ref get_private_field) => { - get_private_field.to_interned_string(interner) - } - Self::GetField(ref get_field) => get_field.to_interned_string(interner), - Self::GetSuperField(ref get_super_field) => { - get_super_field.to_interned_string(interner) - } - Self::WhileLoop(ref while_loop) => while_loop.to_indented_string(interner, indentation), - Self::DoWhileLoop(ref do_while) => do_while.to_indented_string(interner, indentation), - Self::If(ref if_smt) => if_smt.to_indented_string(interner, indentation), - Self::Switch(ref switch) => switch.to_indented_string(interner, indentation), - Self::Object(ref obj) => obj.to_indented_string(interner, indentation), - Self::ArrayDecl(ref arr) => arr.to_interned_string(interner), - Self::VarDeclList(ref list) => list.to_interned_string(interner), - Self::FunctionDecl(ref decl) => decl.to_indented_string(interner, indentation), - Self::FunctionExpr(ref expr) => expr.to_indented_string(interner, indentation), - Self::ArrowFunctionDecl(ref decl) => decl.to_indented_string(interner, indentation), - Self::BinOp(ref op) => op.to_interned_string(interner), - Self::UnaryOp(ref op) => op.to_interned_string(interner), - Self::Return(ref ret) => ret.to_interned_string(interner), - Self::TaggedTemplate(ref template) => template.to_interned_string(interner), - Self::TemplateLit(ref template) => template.to_interned_string(interner), - Self::Throw(ref throw) => throw.to_interned_string(interner), - Self::Assign(ref op) => op.to_interned_string(interner), - Self::LetDeclList(ref decl) | Self::ConstDeclList(ref decl) => { - decl.to_interned_string(interner) - } - Self::AsyncFunctionDecl(ref decl) => decl.to_indented_string(interner, indentation), - Self::AsyncFunctionExpr(ref expr) => expr.to_indented_string(interner, indentation), - Self::AwaitExpr(ref expr) => expr.to_interned_string(interner), - Self::Empty => ";".to_owned(), - Self::Yield(ref y) => y.to_interned_string(interner), - Self::GeneratorDecl(ref decl) => decl.to_interned_string(interner), - Self::GeneratorExpr(ref expr) => expr.to_indented_string(interner, indentation), - Self::AsyncGeneratorExpr(ref expr) => expr.to_indented_string(interner, indentation), - Self::AsyncGeneratorDecl(ref decl) => decl.to_indented_string(interner, indentation), - Self::ClassDecl(ref decl) => decl.to_indented_string(interner, indentation), - Self::ClassExpr(ref expr) => expr.to_indented_string(interner, indentation), - Self::SuperCall(ref super_call) => super_call.to_interned_string(interner), - Self::NewTarget => "new.target".to_owned(), - Self::FormalParameterList(_) => unreachable!(), - } - } - - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { - match self { - Node::Block(block) => { - for node in block.items() { - node.var_declared_names(vars); - } - } - Node::VarDeclList(DeclarationList::Var(declarations)) => { - for declaration in declarations.iter() { - match declaration { - Declaration::Identifier { ident, .. } => { - vars.insert(ident.sym()); - } - Declaration::Pattern(pattern) => { - for ident in pattern.idents() { - vars.insert(ident); - } - } - } - } - } - Node::If(if_statement) => { - if_statement.body().var_declared_names(vars); - if let Some(node) = if_statement.else_node() { - node.var_declared_names(vars); - } - } - Node::DoWhileLoop(do_while_loop) => { - do_while_loop.body().var_declared_names(vars); - } - Node::WhileLoop(while_loop) => { - while_loop.body().var_declared_names(vars); - } - Node::ForLoop(for_loop) => { - if let Some(Node::VarDeclList(DeclarationList::Var(declarations))) = for_loop.init() - { - for declaration in declarations.iter() { - match declaration { - Declaration::Identifier { ident, .. } => { - vars.insert(ident.sym()); - } - Declaration::Pattern(pattern) => { - for ident in pattern.idents() { - vars.insert(ident); - } - } - } - } - } - for_loop.body().var_declared_names(vars); - } - Node::ForInLoop(for_in_loop) => { - if let IterableLoopInitializer::Var(declaration) = for_in_loop.init() { - match declaration { - Declaration::Identifier { ident, .. } => { - vars.insert(ident.sym()); - } - Declaration::Pattern(pattern) => { - for ident in pattern.idents() { - vars.insert(ident); - } - } - } - } - for_in_loop.body().var_declared_names(vars); - } - Node::ForOfLoop(for_of_loop) => { - if let IterableLoopInitializer::Var(declaration) = for_of_loop.init() { - match declaration { - Declaration::Identifier { ident, .. } => { - vars.insert(ident.sym()); - } - Declaration::Pattern(pattern) => { - for ident in pattern.idents() { - vars.insert(ident); - } - } - } - } - for_of_loop.body().var_declared_names(vars); - } - Node::Switch(switch) => { - for case in switch.cases() { - for node in case.body().items() { - node.var_declared_names(vars); - } - } - if let Some(nodes) = switch.default() { - for node in nodes { - node.var_declared_names(vars); - } - } - } - Node::Try(try_statement) => { - for node in try_statement.block().items() { - node.var_declared_names(vars); - } - if let Some(catch) = try_statement.catch() { - for node in catch.block().items() { - node.var_declared_names(vars); - } - } - if let Some(finally) = try_statement.finally() { - for node in finally.items() { - node.var_declared_names(vars); - } - } - } - _ => {} - } - } - - /// Returns true if the node contains a identifier reference named 'arguments'. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments - pub(crate) fn contains_arguments(&self) -> bool { - match self { - Node::Identifier(ident) if ident.sym() == Sym::ARGUMENTS => return true, - Node::ArrayDecl(array) => { - for node in array.as_ref() { - if node.contains_arguments() { - return true; - } - } - } - Node::ArrowFunctionDecl(decl) => { - for node in decl.body().items() { - if node.contains_arguments() { - return true; - } - } - } - Node::Assign(assign) => { - if assign.rhs().contains_arguments() { - return true; - } - } - Node::AwaitExpr(r#await) => { - if r#await.expr().contains_arguments() { - return true; - } - } - Node::BinOp(bin_op) => { - if bin_op.lhs().contains_arguments() || bin_op.rhs().contains_arguments() { - return true; - } - } - Node::Block(block) => { - for node in block.items() { - if node.contains_arguments() { - return true; - } - } - } - Node::Call(call) => { - if call.expr().contains_arguments() { - return true; - } - for node in call.args() { - if node.contains_arguments() { - return true; - } - } - } - Node::ConditionalOp(conditional) => { - if conditional.cond().contains_arguments() { - return true; - } - if conditional.if_true().contains_arguments() { - return true; - } - if conditional.if_false().contains_arguments() { - return true; - } - } - Node::DoWhileLoop(do_while_loop) => { - if do_while_loop.body().contains_arguments() { - return true; - } - if do_while_loop.cond().contains_arguments() { - return true; - } - } - Node::GetConstField(get_const_field) => { - if get_const_field.obj().contains_arguments() { - return true; - } - } - Node::GetPrivateField(get_private_field) => { - if get_private_field.obj().contains_arguments() { - return true; - } - } - Node::GetField(get_field) => { - if get_field.obj().contains_arguments() { - return true; - } - if get_field.field().contains_arguments() { - return true; - } - } - Node::ForLoop(for_loop) => { - if let Some(node) = for_loop.init() { - if node.contains_arguments() { - return true; - } - } - if let Some(node) = for_loop.condition() { - if node.contains_arguments() { - return true; - } - } - if let Some(node) = for_loop.final_expr() { - if node.contains_arguments() { - return true; - } - } - if for_loop.body().contains_arguments() { - return true; - } - } - Node::ForInLoop(for_in_loop) => { - match for_in_loop.init() { - IterableLoopInitializer::Var(declaration) - | IterableLoopInitializer::Let(declaration) - | IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { init, .. } => { - if let Some(init) = init { - { - if init.contains_arguments() { - return true; - } - } - } - } - Declaration::Pattern(pattern) => { - if pattern.contains_arguments() { - return true; - } - } - }, - IterableLoopInitializer::DeclarationPattern(pattern) => { - if pattern.contains_arguments() { - return true; - } - } - IterableLoopInitializer::Identifier(_) => {} - } - if for_in_loop.expr().contains_arguments() { - return true; - } - if for_in_loop.body().contains_arguments() { - return true; - } - } - Node::ForOfLoop(for_of_loop) => { - match for_of_loop.init() { - IterableLoopInitializer::Var(declaration) - | IterableLoopInitializer::Let(declaration) - | IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { init, .. } => { - if let Some(init) = init { - { - if init.contains_arguments() { - return true; - } - } - } - } - Declaration::Pattern(pattern) => { - if pattern.contains_arguments() { - return true; - } - } - }, - IterableLoopInitializer::DeclarationPattern(pattern) => { - if pattern.contains_arguments() { - return true; - } - } - IterableLoopInitializer::Identifier(_) => {} - } - if for_of_loop.iterable().contains_arguments() { - return true; - } - if for_of_loop.body().contains_arguments() { - return true; - } - } - Node::If(r#if) => { - if r#if.cond().contains_arguments() { - return true; - } - if r#if.body().contains_arguments() { - return true; - } - if let Some(node) = r#if.else_node() { - if node.contains_arguments() { - return true; - } - } - } - Node::VarDeclList(decl_list) - | Node::ConstDeclList(decl_list) - | Node::LetDeclList(decl_list) => match decl_list { - DeclarationList::Const(declarations) - | DeclarationList::Let(declarations) - | DeclarationList::Var(declarations) => { - for declaration in declarations.iter() { - match declaration { - Declaration::Identifier { init, .. } => { - if let Some(init) = init { - { - if init.contains_arguments() { - return true; - } - } - } - } - Declaration::Pattern(pattern) => { - if pattern.contains_arguments() { - return true; - } - } - } - } - } - }, - Node::New(new) => { - if new.expr().contains_arguments() { - return true; - } - for node in new.args() { - if node.contains_arguments() { - return true; - } - } - } - Node::Object(object) => { - for property in object.properties() { - match property { - PropertyDefinition::IdentifierReference(ident) => { - if *ident == Sym::ARGUMENTS { - return true; - } - } - PropertyDefinition::Property(_, node) - | PropertyDefinition::SpreadObject(node) => { - if node.contains_arguments() { - return true; - } - } - PropertyDefinition::MethodDefinition(method, _) => match method { - MethodDefinition::Get(function) - | MethodDefinition::Set(function) - | MethodDefinition::Ordinary(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - MethodDefinition::Generator(generator) => { - if let Some(Sym::ARGUMENTS) = generator.name() { - return true; - } - } - MethodDefinition::AsyncGenerator(async_generator) => { - if let Some(Sym::ARGUMENTS) = async_generator.name() { - return true; - } - } - MethodDefinition::Async(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - }, - PropertyDefinition::CoverInitializedName(_, _) => {} - } - } - } - Node::Return(r#return) => { - if let Some(node) = r#return.expr() { - if node.contains_arguments() { - return true; - } - } - } - Node::Switch(r#switch) => { - if r#switch.val().contains_arguments() { - return true; - } - for case in r#switch.cases() { - if case.condition().contains_arguments() { - return true; - } - for node in case.body().items() { - if node.contains_arguments() { - return true; - } - } - } - } - Node::Spread(spread) => { - if spread.val().contains_arguments() { - return true; - } - } - Node::TaggedTemplate(tagged_template) => { - if tagged_template.tag().contains_arguments() { - return true; - } - for node in tagged_template.exprs() { - if node.contains_arguments() { - return true; - } - } - } - Node::TemplateLit(template_lit) => { - for element in template_lit.elements() { - if let template::TemplateElement::Expr(node) = element { - if node.contains_arguments() { - return false; - } - } - } - } - Node::Throw(throw) => { - if throw.expr().contains_arguments() { - return true; - } - } - Node::Try(r#try) => { - for node in r#try.block().items() { - if node.contains_arguments() { - return true; - } - } - if let Some(catch) = r#try.catch() { - for node in catch.block().items() { - if node.contains_arguments() { - return true; - } - } - } - if let Some(finally) = r#try.finally() { - for node in finally.items() { - if node.contains_arguments() { - return true; - } - } - } - } - Node::UnaryOp(unary_op) => { - if unary_op.target().contains_arguments() { - return true; - } - } - Node::WhileLoop(while_loop) => { - if while_loop.cond().contains_arguments() { - return true; - } - if while_loop.body().contains_arguments() { - return true; - } - } - Node::Yield(r#yield) => { - if let Some(node) = r#yield.expr() { - if node.contains_arguments() { - return true; - } - } - } - Node::ClassExpr(class) | Node::ClassDecl(class) => { - if let Some(node) = class.super_ref() { - if node.contains_arguments() { - return true; - } - for element in class.elements() { - match element { - ClassElement::MethodDefinition(_, method) - | ClassElement::StaticMethodDefinition(_, method) => match method { - MethodDefinition::Get(function) - | MethodDefinition::Set(function) - | MethodDefinition::Ordinary(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - MethodDefinition::Generator(generator) => { - if let Some(Sym::ARGUMENTS) = generator.name() { - return true; - } - } - MethodDefinition::AsyncGenerator(async_generator) => { - if let Some(Sym::ARGUMENTS) = async_generator.name() { - return true; - } - } - MethodDefinition::Async(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - }, - ClassElement::FieldDefinition(_, node) - | ClassElement::StaticFieldDefinition(_, node) - | ClassElement::PrivateFieldDefinition(_, node) - | ClassElement::PrivateStaticFieldDefinition(_, node) => { - if let Some(node) = node { - if node.contains_arguments() { - return true; - } - } - } - ClassElement::StaticBlock(statement_list) => { - for node in statement_list.items() { - if node.contains_arguments() { - return true; - } - } - } - _ => {} - } - } - } - } - _ => {} - } - false - } - - /// Returns `true` if the node contains the given token. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - match self { - Node::ArrayDecl(array) => { - for node in array.as_ref() { - if node.contains(symbol) { - return true; - } - } - } - Node::Assign(assign) => { - match assign.lhs() { - AssignTarget::GetPrivateField(field) => { - if field.obj().contains(symbol) { - return true; - } - } - AssignTarget::GetConstField(field) => { - if field.obj().contains(symbol) { - return true; - } - } - AssignTarget::GetField(field) => { - if field.obj().contains(symbol) || field.field().contains(symbol) { - return true; - } - } - AssignTarget::DeclarationPattern(pattern) => { - if pattern.contains(symbol) { - return true; - } - } - AssignTarget::Identifier(_) => {} - } - if assign.rhs().contains(symbol) { - return true; - } - } - Node::AwaitExpr(_) if symbol == ContainsSymbol::AwaitExpression => return true, - Node::AwaitExpr(expr) => { - if expr.expr().contains(symbol) { - return true; - } - } - Node::BinOp(bin_op) => { - if bin_op.lhs().contains(symbol) || bin_op.rhs().contains(symbol) { - return true; - } - } - Node::Block(block) => { - for node in block.items() { - if node.contains(symbol) { - return true; - } - } - } - Node::Call(call) => { - if call.expr().contains(symbol) { - return true; - } - for node in call.args() { - if node.contains(symbol) { - return true; - } - } - } - Node::ConditionalOp(conditional) => { - if conditional.cond().contains(symbol) - || conditional.if_true().contains(symbol) - || conditional.if_false().contains(symbol) - { - return true; - } - } - Node::ConstDeclList(decl_list) - | Node::LetDeclList(decl_list) - | Node::VarDeclList(decl_list) => match decl_list { - DeclarationList::Const(declarations) - | DeclarationList::Let(declarations) - | DeclarationList::Var(declarations) => { - for declaration in declarations.iter() { - if declaration.contains(symbol) { - return true; - } - } - } - }, - Node::DoWhileLoop(do_while_loop) => { - if do_while_loop.cond().contains(symbol) || do_while_loop.body().contains(symbol) { - return true; - } - } - Node::GetConstField(field) => { - if field.obj().contains(symbol) { - return true; - } - } - Node::GetPrivateField(field) => { - if field.obj().contains(symbol) { - return true; - } - } - Node::GetField(field) => { - if field.obj().contains(symbol) || field.field().contains(symbol) { - return true; - } - } - Node::ForLoop(for_loop) => { - if let Some(node) = for_loop.init() { - if node.contains(symbol) { - return true; - } - } - if let Some(node) = for_loop.condition() { - if node.contains(symbol) { - return true; - } - } - if let Some(node) = for_loop.final_expr() { - if node.contains(symbol) { - return true; - } - } - if for_loop.body().contains(symbol) { - return true; - } - } - Node::ForInLoop(for_in_loop) => { - match for_in_loop.init() { - IterableLoopInitializer::Var(declaration) - | IterableLoopInitializer::Let(declaration) - | IterableLoopInitializer::Const(declaration) => { - if declaration.contains(symbol) { - return true; - } - } - IterableLoopInitializer::DeclarationPattern(pattern) => { - if pattern.contains(symbol) { - return true; - } - } - IterableLoopInitializer::Identifier(_) => {} - } - if for_in_loop.body().contains(symbol) || for_in_loop.expr().contains(symbol) { - return true; - } - } - Node::ForOfLoop(for_of_loop) => { - match for_of_loop.init() { - IterableLoopInitializer::Var(declaration) - | IterableLoopInitializer::Let(declaration) - | IterableLoopInitializer::Const(declaration) => { - if declaration.contains(symbol) { - return true; - } - } - IterableLoopInitializer::DeclarationPattern(pattern) => { - if pattern.contains(symbol) { - return true; - } - } - IterableLoopInitializer::Identifier(_) => {} - } - if for_of_loop.body().contains(symbol) || for_of_loop.iterable().contains(symbol) { - return true; - } - } - Node::If(if_node) => { - if if_node.cond().contains(symbol) || if_node.body().contains(symbol) { - return true; - } - if let Some(node) = if_node.else_node() { - if node.contains(symbol) { - return true; - } - } - } - Node::New(new) => { - if new.call().expr().contains(symbol) { - return true; - } - for node in new.call().args() { - if node.contains(symbol) { - return true; - } - } - } - Node::Return(expr) => { - if let Some(expr) = expr.expr() { - if expr.contains(symbol) { - return true; - } - } - } - Node::Switch(switch) => { - if switch.val().contains(symbol) { - return true; - } - for case in switch.cases() { - if case.condition().contains(symbol) { - return true; - } - for node in case.body().items() { - if node.contains(symbol) { - return true; - } - } - } - if let Some(default) = switch.default() { - for node in default { - if node.contains(symbol) { - return true; - } - } - } - } - Node::Spread(spread) => { - if spread.val().contains(symbol) { - return true; - } - } - Node::TaggedTemplate(template) => { - if template.tag().contains(symbol) { - return true; - } - for node in template.exprs() { - if node.contains(symbol) { - return true; - } - } - } - Node::TemplateLit(template) => { - for element in template.elements() { - if let template::TemplateElement::Expr(node) = element { - if node.contains(symbol) { - return true; - } - } - } - } - Node::Throw(expr) => { - if expr.expr().contains(symbol) { - return true; - } - } - Node::Try(try_node) => { - for node in try_node.block().items() { - if node.contains(symbol) { - return true; - } - } - if let Some(catch) = try_node.catch() { - if let Some(declaration) = catch.parameter() { - if declaration.contains(symbol) { - return true; - } - } - for node in catch.block().items() { - if node.contains(symbol) { - return true; - } - } - } - } - Node::UnaryOp(unary) => { - if unary.target().contains(symbol) { - return true; - } - } - Node::WhileLoop(while_loop) => { - if while_loop.cond().contains(symbol) || while_loop.body().contains(symbol) { - return true; - } - } - Node::SuperCall(_) if symbol == ContainsSymbol::SuperCall => return true, - Node::GetSuperField(_) if symbol == ContainsSymbol::SuperProperty => return true, - Node::ArrowFunctionDecl(arrow) => { - for parameter in arrow.params().parameters.iter() { - if parameter.declaration().contains(symbol) { - return true; - } - } - for node in arrow.body().items() { - if node.contains(symbol) { - return true; - } - } - } - Node::Object(object) => { - for property in object.properties() { - match property { - PropertyDefinition::Property(name, init) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - if init.contains(symbol) { - return true; - } - } - PropertyDefinition::SpreadObject(spread) => { - if spread.contains(symbol) { - return true; - } - } - PropertyDefinition::MethodDefinition(_, name) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - } - PropertyDefinition::IdentifierReference(_) - | PropertyDefinition::CoverInitializedName(_, _) => {} - } - } - } - Node::ClassDecl(class) | Node::ClassExpr(class) => { - if let Some(node) = class.super_ref() { - if node.contains(symbol) { - return true; - } - } - for element in class.elements() { - match element { - ClassElement::MethodDefinition(name, _) - | ClassElement::StaticMethodDefinition(name, _) - | ClassElement::FieldDefinition(name, _) - | ClassElement::StaticFieldDefinition(name, _) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - } - _ => {} - } - } - } - Node::Yield(_) if symbol == ContainsSymbol::YieldExpression => return true, - Node::NewTarget if symbol == ContainsSymbol::NewTarget => return true, - _ => {} - } - false - } -} - -/// Represents the possible symbols that can be use the the `Node.contains` function. -#[derive(Clone, Copy, Debug, PartialEq)] -pub(crate) enum ContainsSymbol { - SuperProperty, - SuperCall, - YieldExpression, - AwaitExpression, - NewTarget, -} - -impl ToInternedString for Node { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} - -/// Utility to join multiple Nodes into a single string. -fn join_nodes(interner: &Interner, nodes: &[N]) -> String -where - N: ToInternedString, -{ - let mut first = true; - let mut buf = String::new(); - for e in nodes { - if first { - first = false; - } else { - buf.push_str(", "); - } - buf.push_str(&e.to_interned_string(interner)); - } - buf -} - -/// 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(); - - 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)"); - } -} - -/// 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.declaration().contains(ContainsSymbol::SuperCall) - || param.declaration().contains(ContainsSymbol::SuperProperty) - { - return true; - } - } - for node in body.items() { - 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.declaration().contains(ContainsSymbol::SuperCall) { - return true; - } - } - for node in body.items() { - if node.contains(ContainsSymbol::SuperCall) { - return true; - } - } - false -} diff --git a/boa_engine/src/syntax/ast/node/new/tests.rs b/boa_engine/src/syntax/ast/node/new/tests.rs deleted file mode 100644 index bd9a233f7ce..00000000000 --- a/boa_engine/src/syntax/ast/node/new/tests.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - function MyClass() {}; - let inst = new MyClass(); - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/node/operator/assign/mod.rs b/boa_engine/src/syntax/ast/node/operator/assign/mod.rs deleted file mode 100644 index 403a1cbdd79..00000000000 --- a/boa_engine/src/syntax/ast/node/operator/assign/mod.rs +++ /dev/null @@ -1,440 +0,0 @@ -use crate::syntax::{ - ast::node::{ - declaration::{ - BindingPatternTypeArray, BindingPatternTypeObject, DeclarationPatternArray, - DeclarationPatternObject, - }, - field::get_private_field::GetPrivateField, - object::{PropertyDefinition, PropertyName}, - ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object, - }, - parser::RESERVED_IDENTIFIERS_STRICT, -}; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// An assignment operator assigns a value to its left operand based on the value of its right -/// operand. -/// -/// Assignment operator (`=`), assigns the value of its right operand to its left operand. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct Assign { - lhs: Box, - rhs: Box, -} - -impl Assign { - /// Creates an `Assign` AST node. - pub(in crate::syntax) fn new(lhs: L, rhs: R) -> Self - where - L: Into, - R: Into, - { - Self { - lhs: Box::new(lhs.into()), - rhs: Box::new(rhs.into()), - } - } - - /// Gets the left hand side of the assignment operation. - pub fn lhs(&self) -> &AssignTarget { - &self.lhs - } - - /// Gets the right hand side of the assignment operation. - pub fn rhs(&self) -> &Node { - &self.rhs - } -} - -impl ToInternedString for Assign { - fn to_interned_string(&self, interner: &Interner) -> String { - format!( - "{} = {}", - self.lhs.to_interned_string(interner), - self.rhs.to_interned_string(interner) - ) - } -} - -impl From for Node { - fn from(op: Assign) -> Self { - Self::Assign(op) - } -} - -/// This type represents all valid left-had-side expressions of an assignment operator. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum AssignTarget { - Identifier(Identifier), - GetPrivateField(GetPrivateField), - GetConstField(GetConstField), - GetField(GetField), - DeclarationPattern(DeclarationPattern), -} - -impl AssignTarget { - /// Converts the left-hand-side node of an assignment expression into it's an [`AssignTarget`]. - /// Returns `None` if the given node is an invalid left-hand-side for a assignment expression. - pub(crate) fn from_node(node: &Node, strict: bool) -> Option { - match node { - Node::Identifier(target) => Some(Self::Identifier(*target)), - Node::GetPrivateField(target) => Some(Self::GetPrivateField(target.clone())), - Node::GetConstField(target) => Some(Self::GetConstField(target.clone())), - Node::GetField(target) => Some(Self::GetField(target.clone())), - Node::Object(object) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?; - Some(Self::DeclarationPattern(pattern)) - } - Node::ArrayDecl(array) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?; - Some(Self::DeclarationPattern(pattern)) - } - _ => None, - } - } -} - -impl ToInternedString for AssignTarget { - fn to_interned_string(&self, interner: &Interner) -> String { - match self { - AssignTarget::Identifier(target) => target.to_interned_string(interner), - AssignTarget::GetPrivateField(target) => target.to_interned_string(interner), - AssignTarget::GetConstField(target) => target.to_interned_string(interner), - AssignTarget::GetField(target) => target.to_interned_string(interner), - AssignTarget::DeclarationPattern(target) => target.to_interned_string(interner), - } - } -} - -impl From for AssignTarget { - fn from(target: Identifier) -> Self { - Self::Identifier(target) - } -} - -impl From for AssignTarget { - fn from(target: GetConstField) -> Self { - Self::GetConstField(target) - } -} - -impl From for AssignTarget { - fn from(target: GetField) -> Self { - Self::GetField(target) - } -} - -/// Converts an object literal into an object declaration pattern. -pub(crate) fn object_decl_to_declaration_pattern( - object: &Object, - 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) { - return None; - } - - excluded_keys.push(*ident); - bindings.push(BindingPatternTypeObject::SingleName { - ident: *ident, - property_name: PropertyName::Literal(*ident), - default_init: None, - }); - } - PropertyDefinition::Property(name, node) => match (name, node) { - (PropertyName::Literal(name), Node::Identifier(ident)) if *name == ident.sym() => { - if strict && *name == Sym::EVAL { - return None; - } - if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) { - return None; - } - - excluded_keys.push(*name); - bindings.push(BindingPatternTypeObject::SingleName { - ident: *name, - property_name: PropertyName::Literal(*name), - default_init: None, - }); - } - (PropertyName::Literal(name), Node::Identifier(ident)) => { - bindings.push(BindingPatternTypeObject::SingleName { - ident: ident.sym(), - property_name: PropertyName::Literal(*name), - default_init: None, - }); - } - (PropertyName::Literal(name), Node::Object(object)) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?; - bindings.push(BindingPatternTypeObject::BindingPattern { - ident: PropertyName::Literal(*name), - pattern, - default_init: None, - }); - } - (PropertyName::Literal(name), Node::ArrayDecl(array)) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?; - bindings.push(BindingPatternTypeObject::BindingPattern { - ident: PropertyName::Literal(*name), - pattern, - default_init: None, - }); - } - (_, Node::Assign(assign)) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - if let Some(name) = name.literal() { - if name == ident.sym() { - if strict && name == Sym::EVAL { - return None; - } - if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { - return None; - } - excluded_keys.push(name); - bindings.push(BindingPatternTypeObject::SingleName { - ident: name, - property_name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); - } else { - bindings.push(BindingPatternTypeObject::SingleName { - ident: ident.sym(), - property_name: PropertyName::Literal(name), - default_init: Some(assign.rhs().clone()), - }); - } - } else { - return None; - } - } - AssignTarget::DeclarationPattern(pattern) => { - bindings.push(BindingPatternTypeObject::BindingPattern { - ident: name.clone(), - pattern: pattern.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::GetConstField(field) => { - bindings.push(BindingPatternTypeObject::AssignmentGetConstField { - property_name: name.clone(), - get_const_field: field.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::GetField(field) => { - bindings.push(BindingPatternTypeObject::AssignmentGetField { - property_name: name.clone(), - get_field: field.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::GetPrivateField(_) => return None, - }, - (_, Node::GetConstField(field)) => { - bindings.push(BindingPatternTypeObject::AssignmentGetConstField { - property_name: name.clone(), - get_const_field: field.clone(), - default_init: None, - }); - } - (_, Node::GetField(field)) => { - bindings.push(BindingPatternTypeObject::AssignmentGetField { - property_name: name.clone(), - get_field: field.clone(), - default_init: None, - }); - } - (PropertyName::Computed(name), Node::Identifier(ident)) => { - bindings.push(BindingPatternTypeObject::SingleName { - ident: ident.sym(), - property_name: PropertyName::Computed(name.clone()), - default_init: None, - }); - } - _ => return None, - }, - PropertyDefinition::SpreadObject(spread) => { - match spread { - Node::Identifier(ident) => { - bindings.push(BindingPatternTypeObject::RestProperty { - ident: ident.sym(), - excluded_keys: excluded_keys.clone(), - }); - } - Node::GetConstField(get_const_field) => { - bindings.push(BindingPatternTypeObject::AssignmentRestProperty { - get_const_field: get_const_field.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 && (*ident == Sym::EVAL || *ident == Sym::ARGUMENTS) { - return None; - } - - bindings.push(BindingPatternTypeObject::SingleName { - ident: *ident, - property_name: PropertyName::Literal(*ident), - default_init: Some(expr.clone()), - }); - } - } - } - if object.properties().is_empty() { - bindings.push(BindingPatternTypeObject::Empty); - } - Some(DeclarationPattern::Object(DeclarationPatternObject::new( - bindings, None, - ))) -} - -/// Converts an array declaration into an array declaration pattern. -pub(crate) fn array_decl_to_declaration_pattern( - array: &ArrayDecl, - strict: bool, -) -> Option { - if array.has_trailing_comma_spread() { - return None; - } - - let mut bindings = Vec::new(); - for (i, node) in array.as_ref().iter().enumerate() { - match node { - Node::Identifier(ident) => { - if strict && ident.sym() == Sym::ARGUMENTS { - return None; - } - - bindings.push(BindingPatternTypeArray::SingleName { - ident: ident.sym(), - default_init: None, - }); - } - Node::Spread(spread) => { - match spread.val() { - Node::Identifier(ident) => { - bindings - .push(BindingPatternTypeArray::SingleNameRest { ident: ident.sym() }); - } - Node::GetField(get_field) => { - bindings.push(BindingPatternTypeArray::GetFieldRest { - get_field: get_field.clone(), - }); - } - Node::GetConstField(get_const_field) => { - bindings.push(BindingPatternTypeArray::GetConstFieldRest { - get_const_field: get_const_field.clone(), - }); - } - Node::ArrayDecl(array) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?; - bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern }); - } - Node::Object(object) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?; - bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern }); - } - _ => return None, - } - if i + 1 != array.as_ref().len() { - return None; - } - } - Node::Empty => { - bindings.push(BindingPatternTypeArray::Elision); - } - Node::Assign(assign) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - bindings.push(BindingPatternTypeArray::SingleName { - ident: ident.sym(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::GetConstField(get_const_field) => { - bindings.push(BindingPatternTypeArray::GetConstField { - get_const_field: get_const_field.clone(), - }); - } - AssignTarget::GetField(get_field) => { - bindings.push(BindingPatternTypeArray::GetField { - get_field: get_field.clone(), - }); - } - AssignTarget::DeclarationPattern(pattern) => match pattern { - DeclarationPattern::Object(pattern) => { - let mut pattern = pattern.clone(); - if pattern.init.is_none() { - pattern.init = Some(assign.rhs().clone()); - } - bindings.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Object(pattern), - }); - } - DeclarationPattern::Array(pattern) => { - let mut pattern = pattern.clone(); - if pattern.init.is_none() { - pattern.init = Some(assign.rhs().clone()); - } - bindings.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Array(pattern), - }); - } - }, - AssignTarget::GetPrivateField(_) => return None, - }, - Node::ArrayDecl(array) => { - let pattern = array_decl_to_declaration_pattern(array, strict)?; - bindings.push(BindingPatternTypeArray::BindingPattern { pattern }); - } - Node::Object(object) => { - let pattern = object_decl_to_declaration_pattern(object, strict)?; - bindings.push(BindingPatternTypeArray::BindingPattern { pattern }); - } - Node::GetField(get_field) => { - bindings.push(BindingPatternTypeArray::GetField { - get_field: get_field.clone(), - }); - } - Node::GetConstField(get_const_field) => { - bindings.push(BindingPatternTypeArray::GetConstField { - get_const_field: get_const_field.clone(), - }); - } - _ => return None, - } - } - Some(DeclarationPattern::Array(DeclarationPatternArray::new( - bindings, None, - ))) -} diff --git a/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs b/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs deleted file mode 100644 index ea53b6ad2d8..00000000000 --- a/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::syntax::ast::{node::Node, op}; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// Binary operators requires two operands, one before the operator and one after the operator. -/// -/// More information: -/// - [MDN documentation][mdn] -/// -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct BinOp { - op: op::BinOp, - lhs: Box, - rhs: Box, -} - -impl BinOp { - /// Creates a `BinOp` AST node. - pub(in crate::syntax) fn new(op: O, lhs: L, rhs: R) -> Self - where - O: Into, - L: Into, - R: Into, - { - Self { - op: op.into(), - lhs: Box::new(lhs.into()), - rhs: Box::new(rhs.into()), - } - } - - /// Gets the binary operation of the node. - pub fn op(&self) -> op::BinOp { - self.op - } - - /// Gets the left hand side of the binary operation. - pub fn lhs(&self) -> &Node { - &self.lhs - } - - /// Gets the right hand side of the binary operation. - pub fn rhs(&self) -> &Node { - &self.rhs - } -} - -impl ToInternedString for BinOp { - 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 Node { - fn from(op: BinOp) -> Self { - Self::BinOp(op) - } -} diff --git a/boa_engine/src/syntax/ast/node/operator/mod.rs b/boa_engine/src/syntax/ast/node/operator/mod.rs deleted file mode 100644 index 2efae8c73d6..00000000000 --- a/boa_engine/src/syntax/ast/node/operator/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Operator nodes - -pub mod assign; -pub mod bin_op; -pub mod unary_op; - -pub use self::{assign::Assign, bin_op::BinOp, unary_op::UnaryOp}; - -#[cfg(test)] -mod tests; diff --git a/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs b/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs deleted file mode 100644 index 51c6c9f87e2..00000000000 --- a/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::syntax::ast::{node::Node, op}; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// A unary operation is an operation with only one operand. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct UnaryOp { - op: op::UnaryOp, - target: Box, -} - -impl UnaryOp { - /// Creates a new `UnaryOp` AST node. - pub(in crate::syntax) fn new(op: op::UnaryOp, target: V) -> Self - where - V: Into, - { - Self { - op, - target: Box::new(target.into()), - } - } - - /// Gets the unary operation of the node. - pub fn op(&self) -> op::UnaryOp { - self.op - } - - /// Gets the target of this unary operator. - pub fn target(&self) -> &Node { - self.target.as_ref() - } -} - -impl ToInternedString for UnaryOp { - fn to_interned_string(&self, interner: &Interner) -> String { - format!("{}{}", self.op, self.target.to_interned_string(interner)) - } -} - -impl From for Node { - fn from(op: UnaryOp) -> Self { - Self::UnaryOp(op) - } -} diff --git a/boa_engine/src/syntax/ast/node/return_smt/tests.rs b/boa_engine/src/syntax/ast/node/return_smt/tests.rs deleted file mode 100644 index 8e4ecb1f5d8..00000000000 --- a/boa_engine/src/syntax/ast/node/return_smt/tests.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[test] -fn fmt() { - super::super::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/node/spread/mod.rs b/boa_engine/src/syntax/ast/node/spread/mod.rs deleted file mode 100644 index 75b369fc109..00000000000 --- a/boa_engine/src/syntax/ast/node/spread/mod.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - -/// The `spread` operator allows an iterable such as an array expression or string to be -/// expanded. -/// -/// Syntax: `...x` -/// -/// It expands array expressions or strings in places where zero or more arguments (for -/// function calls) or elements (for array literals) -/// are expected, or an object expression to be expanded in places where zero or more key-value -/// pairs (for object literals) are expected. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[cfg_attr(feature = "deser", serde(transparent))] -#[derive(Clone, Debug, PartialEq)] -pub struct Spread { - val: Box, -} - -impl Spread { - pub fn val(&self) -> &Node { - &self.val - } - - /// Creates a `Spread` AST node. - pub fn new(val: V) -> Self - where - V: Into, - { - Self { - val: Box::new(val.into()), - } - } -} - -impl ToInternedString for Spread { - fn to_interned_string(&self, interner: &Interner) -> String { - format!("...{}", self.val().to_interned_string(interner)) - } -} - -impl From for Node { - fn from(spread: Spread) -> Self { - Self::Spread(spread) - } -} diff --git a/boa_engine/src/syntax/ast/node/spread/tests.rs b/boa_engine/src/syntax/ast/node/spread/tests.rs deleted file mode 100644 index 04d554d65e1..00000000000 --- a/boa_engine/src/syntax/ast/node/spread/tests.rs +++ /dev/null @@ -1,47 +0,0 @@ -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() { - super::super::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/node/super_call/mod.rs b/boa_engine/src/syntax/ast/node/super_call/mod.rs deleted file mode 100644 index 3167663bb17..00000000000 --- a/boa_engine/src/syntax/ast/node/super_call/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::syntax::ast::node::{join_nodes, Node}; -use boa_interner::{Interner, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// The `super` keyword is used to access and call functions on an object's parent. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct SuperCall { - args: Box<[Node]>, -} - -impl SuperCall { - /// Creates a new `SuperCall` AST node. - pub(crate) fn new(args: A) -> Self - where - A: Into>, - { - Self { args: args.into() } - } - - /// Retrieves the arguments of the super call. - pub(crate) fn args(&self) -> &[Node] { - &self.args - } -} - -impl ToInternedString for SuperCall { - fn to_interned_string(&self, interner: &Interner) -> String { - format!("super({})", join_nodes(interner, &self.args)) - } -} - -impl From for Node { - fn from(call: SuperCall) -> Self { - Self::SuperCall(call) - } -} diff --git a/boa_engine/src/syntax/ast/node/template/mod.rs b/boa_engine/src/syntax/ast/node/template/mod.rs deleted file mode 100644 index 75c003d3e4f..00000000000 --- a/boa_engine/src/syntax/ast/node/template/mod.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! Template literal node. - -use crate::string::ToStringEscaped; - -use super::Node; -use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - -/// Template literals are string literals allowing embedded expressions. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct TemplateLit { - elements: Box<[TemplateElement]>, -} - -impl TemplateLit { - pub fn new(elements: E) -> Self - where - E: Into>, - { - Self { - elements: elements.into(), - } - } - - pub(crate) fn elements(&self) -> &[TemplateElement] { - &self.elements - } -} - -impl ToInternedString for TemplateLit { - fn to_interned_string(&self, interner: &Interner) -> String { - let mut buf = "`".to_owned(); - - for elt in self.elements.iter() { - match elt { - TemplateElement::String(s) => interner.resolve_expect(*s).join_with_context( - |s, buf| buf.push_str(s), - |js, buf| buf.push_str(&js.to_string_escaped()), - &mut buf, - true, - ), - TemplateElement::Expr(n) => { - buf.push_str(&format!("${{{}}}", n.to_interned_string(interner))); - } - } - } - buf.push('`'); - - buf - } -} -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub struct TaggedTemplate { - tag: Box, - raws: Box<[Sym]>, - cookeds: Box<[Option]>, - exprs: Box<[Node]>, -} - -impl TaggedTemplate { - /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and - /// the expressions. - pub fn new(tag: Node, raws: R, cookeds: C, exprs: E) -> Self - where - R: Into>, - C: Into]>>, - E: Into>, - { - Self { - tag: Box::new(tag), - raws: raws.into(), - cookeds: cookeds.into(), - exprs: exprs.into(), - } - } - - pub(crate) fn tag(&self) -> &Node { - &self.tag - } - - pub(crate) fn raws(&self) -> &[Sym] { - &self.raws - } - - pub(crate) fn cookeds(&self) -> &[Option] { - &self.cookeds - } - - pub(crate) fn exprs(&self) -> &[Node] { - &self.exprs - } -} - -impl ToInternedString for TaggedTemplate { - fn to_interned_string(&self, interner: &Interner) -> String { - let mut buf = format!("{}`", self.tag.to_interned_string(interner)); - for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { - buf.push_str(&format!( - "{}${{{}}}", - interner.resolve_expect(raw), - expr.to_interned_string(interner) - )); - } - buf.push('`'); - - buf - } -} - -impl From for Node { - fn from(template: TaggedTemplate) -> Self { - Self::TaggedTemplate(Box::new(template)) - } -} - -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] -pub enum TemplateElement { - String(Sym), - Expr(Node), -} diff --git a/boa_engine/src/syntax/ast/node/template/tests.rs b/boa_engine/src/syntax/ast/node/template/tests.rs deleted file mode 100644 index 1696c29fcc0..00000000000 --- a/boa_engine/src/syntax/ast/node/template/tests.rs +++ /dev/null @@ -1,48 +0,0 @@ -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 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 fmt() { - super::super::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/node/throw/tests.rs b/boa_engine/src/syntax/ast/node/throw/tests.rs deleted file mode 100644 index 076352822f2..00000000000 --- a/boa_engine/src/syntax/ast/node/throw/tests.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[test] -fn fmt() { - super::super::test_formatting( - r#" - try { - throw "hello"; - } catch(e) { - console.log(e); - }; - "#, - ); -} diff --git a/boa_engine/src/syntax/ast/op.rs b/boa_engine/src/syntax/ast/op.rs deleted file mode 100644 index 94a6f22dd9b..00000000000 --- a/boa_engine/src/syntax/ast/op.rs +++ /dev/null @@ -1,1025 +0,0 @@ -//! This module implements various structure for logic handling. - -use std::fmt::{Display, Formatter, Result}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -/// Arithmetic operators take numerical values (either literals or variables) -/// as their operands and return a single numerical value. -/// -/// More information: -/// - [MDN documentation][mdn] -/// -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum NumOp { - /// The addition operator produces the sum of numeric operands or string concatenation. - /// - /// Syntax: `x + y` - /// - /// More information: - /// - [ECMAScript reference][spec]. - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition - Add, - - /// The subtraction operator subtracts the two operands, producing their difference. - /// - /// Syntax: `x - y` - /// - /// More information: - /// - [ECMAScript reference][spec]. - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction - Sub, - - /// The division operator produces the quotient of its operands where the left operand - /// is the dividend and the right operand is the divisor. - /// - /// Syntax: `x / y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division - Div, - - /// The multiplication operator produces the product of the operands. - /// - /// Syntax: `x * y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication - Mul, - - /// The exponentiation operator returns the result of raising the first operand to - /// the power of the second operand. - /// - /// Syntax: `x ** y` - /// - /// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c). - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-exp-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation - Exp, - - /// The remainder operator returns the remainder left over when one operand is divided by a second operand. - /// - /// Syntax: `x % y` - /// - /// The remainder operator always takes the sign of the dividend. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder - Mod, -} - -impl NumOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::Add => "+", - Self::Sub => "-", - Self::Div => "/", - Self::Mul => "*", - Self::Exp => "**", - Self::Mod => "%", - } - } -} - -impl Display for NumOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// A unary operator is one that takes a single operand/argument and performs an operation. -/// -/// A unary operation is an operation with only one operand. This operand comes either -/// before or after the operator. Unary operators are more efficient than standard JavaScript -/// function calls. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum UnaryOp { - /// The increment operator increments (adds one to) its operand and returns a value. - /// - /// Syntax: `++x` - /// - /// This operator increments and returns the value after incrementing. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment - IncrementPost, - - /// The increment operator increments (adds one to) its operand and returns a value. - /// - /// Syntax: `x++` - /// - /// This operator increments and returns the value before incrementing. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment - IncrementPre, - - /// The decrement operator decrements (subtracts one from) its operand and returns a value. - /// - /// Syntax: `--x` - /// - /// This operator decrements and returns the value before decrementing. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement - DecrementPost, - - /// The decrement operator decrements (subtracts one from) its operand and returns a value. - /// - /// Syntax: `x--` - /// - /// This operator decrements the operand and returns the value after decrementing. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement - DecrementPre, - - /// The unary negation operator precedes its operand and negates it. - /// - /// Syntax: `-x` - /// - /// Converts non-numbers data types to numbers like unary plus, - /// however, it performs an additional operation, negation. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation - Minus, - - /// The unary plus operator attempts to convert the operand into a number, if it isn't already. - /// - /// Syntax: `+x` - /// - /// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred - /// way of converting something into a number, because it does not perform any other operations on the number. - /// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus - Plus, - - /// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`. - /// - /// Syntax: `!x` - /// - /// Boolean values simply get inverted: `!true === false` and `!false === true`. - /// Non-boolean values get converted to boolean values first, then are negated. - /// This means that it is possible to use a couple of NOT operators in series to explicitly - /// force the conversion of any value to the corresponding boolean primitive. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT - Not, - - /// Performs the NOT operator on each bit. - /// - /// Syntax: `~x` - /// - /// NOT `a` yields the inverted value (or one's complement) of `a`. - /// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT - Tilde, - - /// The `typeof` operator returns a string indicating the type of the unevaluated operand. - /// - /// Syntax: `typeof x` or `typeof(x)` - /// - /// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it. - /// You can use this to validate function parameters or check if variables are defined. - /// There are other uses as well. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof - TypeOf, - - /// The JavaScript `delete` operator removes a property from an object. - /// - /// Syntax: `delete x` - /// - /// Unlike what common belief suggests, the delete operator has nothing to do with - /// directly freeing memory. Memory management is done indirectly via breaking references. - /// If no more references to the same property are held, it is eventually released automatically. - /// - /// The `delete` operator returns `true` for all cases except when the property is an - /// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) - /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete) - /// property, in which case, `false` is returned in non-strict mode. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-delete-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete - Delete, - - /// The `void` operator evaluates the given `expression` and then returns `undefined`. - /// - /// Syntax: `void x` - /// - /// This operator allows evaluating expressions that produce a value into places where an - /// expression that evaluates to `undefined` is desired. - /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)` - /// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used. - /// - /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), - /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-void-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void - Void, -} - -impl UnaryOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::IncrementPost | Self::IncrementPre => "++", - Self::DecrementPost | Self::DecrementPre => "--", - Self::Plus => "+", - Self::Minus => "-", - Self::Not => "!", - Self::Tilde => "~", - Self::Delete => "delete", - Self::TypeOf => "typeof", - Self::Void => "void", - } - } -} - -impl Display for UnaryOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// A bitwise operator is an operator used to perform bitwise operations -/// on bit patterns or binary numerals that involve the manipulation of individual bits. -/// -/// More information: -/// - [MDN documentation][mdn] -/// -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum BitOp { - /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1. - /// - /// Syntax: `x & y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND - And, - - /// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1. - /// - /// Syntax: `x | y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR - Or, - - /// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different. - /// - /// Syntax: `x ^ y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR - Xor, - - /// This operator shifts the first operand the specified number of bits to the left. - /// - /// Syntax: `x << y` - /// - /// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift - Shl, - - /// This operator shifts the first operand the specified number of bits to the right. - /// - /// Syntax: `x >> y` - /// - /// Excess bits shifted off to the right are discarded. Copies of the leftmost bit - /// are shifted in from the left. Since the new leftmost bit has the same value as - /// the previous leftmost bit, the sign bit (the leftmost bit) does not change. - /// Hence the name "sign-propagating". - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift - Shr, - - /// This operator shifts the first operand the specified number of bits to the right. - /// - /// Syntax: `x >>> y` - /// - /// Excess bits shifted off to the right are discarded. Zero bits are shifted in - /// from the left. The sign bit becomes 0, so the result is always non-negative. - /// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift - UShr, -} - -impl BitOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::And => "&", - Self::Or => "|", - Self::Xor => "^", - Self::Shl => "<<", - Self::Shr => ">>", - Self::UShr => ">>>", - } - } -} - -impl Display for BitOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// A comparison operator compares its operands and returns a logical value based on whether the comparison is true. -/// -/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard -/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type, -/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in -/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==` -/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands -/// to compatible types before checking equality. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CompOp { - /// The equality operator converts the operands if they are not of the same type, then applies - /// strict comparison. - /// - /// Syntax: `y == y` - /// - /// If both operands are objects, then JavaScript compares internal references which are equal - /// when operands refer to the same object in memory. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality - Equal, - - /// The inequality operator returns `true` if the operands are not equal. - /// - /// Syntax: `x != y` - /// - /// If the two operands are not of the same type, JavaScript attempts to convert the operands - /// to an appropriate type for the comparison. If both operands are objects, then JavaScript - /// compares internal references which are not equal when operands refer to different objects - /// in memory. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality - NotEqual, - - /// The identity operator returns `true` if the operands are strictly equal **with no type - /// conversion**. - /// - /// Syntax: `x === y` - /// - /// Returns `true` if the operands are equal and of the same type. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity - StrictEqual, - - /// The non-identity operator returns `true` if the operands **are not equal and/or not of the - /// same type**. - /// - /// Syntax: `x !== y` - /// - /// Returns `true` if the operands are of the same type but not equal, or are of different type. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity> - StrictNotEqual, - - /// The greater than operator returns `true` if the left operand is greater than the right - /// operand. - /// - /// Syntax: `x > y` - /// - /// Returns `true` if the left operand is greater than the right operand. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator - GreaterThan, - - /// The greater than or equal operator returns `true` if the left operand is greater than or - /// equal to the right operand. - /// - /// Syntax: `x >= y` - /// - /// Returns `true` if the left operand is greater than the right operand. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator - GreaterThanOrEqual, - - /// The less than operator returns `true` if the left operand is less than the right operand. - /// - /// Syntax: `x < y` - /// - /// Returns `true` if the left operand is less than the right operand. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator - LessThan, - - /// The less than or equal operator returns `true` if the left operand is less than or equal to - /// the right operand. - /// - /// Syntax: `x <= y` - /// - /// Returns `true` if the left operand is less than or equal to the right operand. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator - LessThanOrEqual, - - /// The `in` operator returns `true` if the specified property is in the specified object or - /// its prototype chain. - /// - /// Syntax: `prop in object` - /// - /// Returns `true` the specified property is in the specified object or its prototype chain. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in - In, - - /// The `instanceof` operator returns `true` if the specified object is an instance of the - /// right hand side object. - /// - /// Syntax: `obj instanceof Object` - /// - /// Returns `true` the `prototype` property of the right hand side constructor appears anywhere - /// in the prototype chain of the object. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof - InstanceOf, -} - -impl CompOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::Equal => "==", - Self::NotEqual => "!=", - Self::StrictEqual => "===", - Self::StrictNotEqual => "!==", - Self::GreaterThan => ">", - Self::GreaterThanOrEqual => ">=", - Self::LessThan => "<", - Self::LessThanOrEqual => "<=", - Self::In => "in", - Self::InstanceOf => "instanceof", - } - } -} - -impl Display for CompOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value. -/// -/// However, the `&&` and `||` operators actually return the value of one of the specified operands, -/// so if these operators are used with non-Boolean values, they may return a non-Boolean value. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum LogOp { - /// The logical AND operator returns the value of the first operand if it can be coerced into `false`; - /// otherwise, it returns the second operand. - /// - /// Syntax: `x && y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND - And, - - /// The logical OR operator returns the value the first operand if it can be coerced into `true`; - /// otherwise, it returns the second operand. - /// - /// Syntax: `x || y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR - Or, - - /// The nullish coalescing operator is a logical operator that returns the second operand - /// when its first operand is null or undefined, and otherwise returns its first operand. - /// - /// Syntax: `x ?? y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator - Coalesce, -} - -impl LogOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::And => "&&", - Self::Or => "||", - Self::Coalesce => "??", - } - } -} - -impl Display for LogOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// This represents a binary operation between two values. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum BinOp { - /// Numeric operation. - /// - /// see: [`NumOp`](enum.NumOp.html) - Num(NumOp), - - /// Bitwise operation. - /// - /// see: [`BitOp`](enum.BitOp.html). - Bit(BitOp), - - /// Comparative operation. - /// - /// see: [`CompOp`](enum.CompOp.html). - Comp(CompOp), - - /// Logical operation. - /// - /// see: [`LogOp`](enum.LogOp.html). - Log(LogOp), - - /// Assign operation. - /// - /// see: [`AssignOp`](enum.AssignOp.html). - Assign(AssignOp), - - /// Comma operation. - Comma, -} - -impl From for BinOp { - fn from(op: NumOp) -> Self { - Self::Num(op) - } -} - -impl From for BinOp { - fn from(op: BitOp) -> Self { - Self::Bit(op) - } -} - -impl From for BinOp { - fn from(op: CompOp) -> Self { - Self::Comp(op) - } -} - -impl From for BinOp { - fn from(op: LogOp) -> Self { - Self::Log(op) - } -} - -impl From for BinOp { - fn from(op: AssignOp) -> Self { - Self::Assign(op) - } -} - -impl BinOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::Num(ref op) => op.as_str(), - Self::Bit(ref op) => op.as_str(), - Self::Comp(ref op) => op.as_str(), - Self::Log(ref op) => op.as_str(), - Self::Assign(ref op) => op.as_str(), - Self::Comma => ",", - } - } -} - -impl Display for BinOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} - -/// An assignment operator assigns a value to its left operand based on the value of its right operand. -/// -/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its -/// left operand. That is, `x = y` assigns the value of `y to x`. -/// -/// There are also compound assignment operators that are shorthand for the operations -/// -/// More information: -/// - [ECMAScript reference][spec] -/// - [MDN documentation][mdn] -/// -/// [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(Serialize, Deserialize))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum AssignOp { - /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable. - /// - /// Syntax: `x += y` - /// - /// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment - Add, - - /// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable. - /// - /// Syntax: `x -= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment - Sub, - - /// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable. - /// - /// Syntax: `x *= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment - Mul, - - /// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable. - /// - /// Syntax: `x /= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment - Div, - - /// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable. - /// - /// Syntax: `x %= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment - Mod, - - /// The exponentiation assignment operator raises the value of a variable to the power of the right operand. - /// - /// Syntax: `x ** y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment - Exp, - - /// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on - /// them and assigns the result to the variable. - /// - /// Syntax: `x &= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment - And, - - /// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on - /// them and assigns the result to the variable. - /// - /// Syntax: `x |= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment - Or, - - /// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on - /// them and assigns the result to the variable. - /// - /// Syntax: `x ^= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment - Xor, - - /// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable. - /// - /// Syntax: `x <<= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment - Shl, - - /// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. - /// - /// Syntax: `x >>= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment - Shr, - - /// The unsigned right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable. - /// - /// Syntax: `x >>>= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment - Ushr, - - /// The logical and assignment operator only assigns if the target variable is truthy. - /// - /// Syntax: `x &&= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment - BoolAnd, - - /// The logical or assignment operator only assigns if the target variable is falsy. - /// - /// Syntax: `x ||= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment - BoolOr, - - /// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined). - /// - /// Syntax: `x ??= y` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment - Coalesce, -} - -impl AssignOp { - /// Retrieves the operation as a static string. - fn as_str(self) -> &'static str { - match self { - Self::Add => "+=", - Self::Sub => "-=", - Self::Mul => "*=", - Self::Exp => "**=", - Self::Div => "/=", - Self::Mod => "%=", - Self::And => "&=", - Self::Or => "|=", - Self::Xor => "^=", - Self::Shl => "<<=", - Self::Shr => ">>=", - Self::Ushr => ">>>=", - Self::BoolAnd => "&&=", - Self::BoolOr => "||=", - Self::Coalesce => "??=", - } - } -} - -impl Display for AssignOp { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.as_str()) - } -} diff --git a/boa_engine/src/syntax/ast/pattern.rs b/boa_engine/src/syntax/ast/pattern.rs new file mode 100644 index 00000000000..e1df60aa585 --- /dev/null +++ b/boa_engine/src/syntax/ast/pattern.rs @@ -0,0 +1,811 @@ +use boa_interner::{Interner, Sym, ToInternedString}; + +use super::{ + expression::access::{PropertyAccess, PropertyAccessField}, + property::PropertyName, + ContainsSymbol, Expression, +}; + +/// `Pattern` represents an object or array pattern. +/// +/// This enum mostly wraps the functionality of the specific binding pattern types. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `BindingPattern`][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-BindingPattern +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum Pattern { + Object(PatternObject), + Array(PatternArray), +} + +impl From for Pattern { + fn from(obj: PatternObject) -> Self { + Pattern::Object(obj) + } +} + +impl From for Pattern { + fn from(obj: PatternArray) -> Self { + Pattern::Array(obj) + } +} + +impl From> for Pattern { + fn from(elements: Vec) -> Self { + PatternObject::new(elements.into()).into() + } +} +impl From> for Pattern { + fn from(elements: Vec) -> Self { + PatternArray::new(elements.into()).into() + } +} + +impl ToInternedString for Pattern { + fn to_interned_string(&self, interner: &Interner) -> String { + match &self { + Pattern::Object(o) => o.to_interned_string(interner), + Pattern::Array(a) => a.to_interned_string(interner), + } + } +} + +impl Pattern { + /// Gets the list of identifiers in the pattern. + /// + /// A single pattern may have 0 to n identifiers. + #[inline] + pub fn idents(&self) -> Vec { + match &self { + Pattern::Object(pattern) => pattern.idents(), + Pattern::Array(pattern) => pattern.idents(), + } + } + + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Pattern::Object(pattern) => pattern.contains_arguments(), + Pattern::Array(array) => array.contains_arguments(), + } + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Pattern::Object(object) => object.contains(symbol), + Pattern::Array(array) => array.contains(symbol), + } + } +} + +/// `DeclarationPatternObject` represents an object binding pattern. +/// +/// This struct holds a list of bindings, and an optional initializer for the binding pattern. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ObjectBindingPattern`][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct PatternObject(Box<[PatternObjectElement]>); + +impl From> for PatternObject { + fn from(elements: Vec) -> Self { + Self(elements.into()) + } +} + +impl ToInternedString for PatternObject { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "{".to_owned(); + for (i, binding) in self.0.iter().enumerate() { + let binding = binding.to_interned_string(interner); + let str = if i == self.0.len() - 1 { + format!("{binding} ") + } else { + format!("{binding},") + }; + + buf.push_str(&str); + } + buf.push('}'); + buf + } +} + +impl PatternObject { + /// Create a new object binding pattern. + #[inline] + pub(in crate::syntax) fn new(bindings: Box<[PatternObjectElement]>) -> Self { + Self(bindings) + } + + /// Gets the bindings for the object binding pattern. + #[inline] + pub(crate) fn bindings(&self) -> &[PatternObjectElement] { + &self.0 + } + + // Returns if the object binding pattern has a rest element. + #[inline] + pub(crate) fn has_rest(&self) -> bool { + matches!( + self.0.last(), + Some(PatternObjectElement::RestProperty { .. }) + ) + } + + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.0.iter().any(PatternObjectElement::contains_arguments) + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + 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(PatternObjectElement::idents) + .collect() + } +} + +/// `DeclarationPatternArray` represents an array binding pattern. +/// +/// This struct holds a list of bindings, and an optional initializer for the binding pattern. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ArrayBindingPattern`][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub struct PatternArray(Box<[PatternArrayElement]>); + +impl From> for PatternArray { + fn from(elements: Vec) -> Self { + Self(elements.into()) + } +} + +impl ToInternedString for PatternArray { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = "[".to_owned(); + for (i, binding) in self.0.iter().enumerate() { + if i == self.0.len() - 1 { + match binding { + PatternArrayElement::Elision => { + buf.push_str(&format!("{}, ", binding.to_interned_string(interner))); + } + _ => buf.push_str(&format!("{} ", binding.to_interned_string(interner))), + } + } else { + buf.push_str(&format!("{},", binding.to_interned_string(interner))); + } + } + buf.push(']'); + buf + } +} + +impl PatternArray { + /// Create a new array binding pattern. + #[inline] + pub(in crate::syntax) fn new(bindings: Box<[PatternArrayElement]>) -> Self { + Self(bindings) + } + + /// Gets the bindings for the array binding pattern. + #[inline] + pub(crate) fn bindings(&self) -> &[PatternArrayElement] { + &self.0 + } + + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.0.iter().any(PatternArrayElement::contains_arguments) + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + 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 { + self.0 + .iter() + .flat_map(PatternArrayElement::idents) + .collect() + } +} + +/// `BindingPatternTypeObject` represents the different types of bindings that an object binding pattern may contain. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ObjectBindingPattern`][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum PatternObjectElement { + /// Empty represents an empty object pattern e.g. `{ }`. + Empty, + + /// SingleName represents one of the following properties: + /// + /// - `SingleName` with an identifier and an optional default initializer. + /// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding + /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty + SingleName { + name: PropertyName, + ident: Sym, + default_init: Option, + }, + + /// RestProperty represents a `BindingRestProperty` with an identifier. + /// + /// It also includes a list of the property keys that should be excluded from the rest, + /// because they where already assigned. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty + RestProperty { ident: Sym, excluded_keys: Vec }, + + /// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. + /// + /// Note: According to the spec this is not part of an ObjectBindingPattern. + /// This is only used when a object literal is used to cover an AssignmentPattern. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty + AssignmentPropertyAccess { + name: PropertyName, + access: PropertyAccess, + default_init: Option, + }, + + /// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget. + /// + /// Note: According to the spec this is not part of an ObjectBindingPattern. + /// This is only used when a object literal is used to cover an AssignmentPattern. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty + AssignmentRestPropertyAccess { + access: PropertyAccess, + excluded_keys: Vec, + }, + + /// Pattern represents a property with a `Pattern` as the element. + /// + /// Additionally to the identifier of the new property and the nested pattern, + /// this may also include an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty + Pattern { + name: PropertyName, + pattern: Pattern, + default_init: Option, + }, +} + +impl PatternObjectElement { + /// Returns true if the element contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + PatternObjectElement::SingleName { + name, default_init, .. + } => { + if let PropertyName::Computed(node) = name { + if node.contains_arguments() { + return true; + } + } + if let Some(init) = default_init { + if init.contains_arguments() { + return true; + } + } + } + PatternObjectElement::AssignmentRestPropertyAccess { access, .. } => { + if access.target().contains_arguments() { + return true; + } + } + PatternObjectElement::Pattern { + name, + pattern, + default_init, + } => { + if let PropertyName::Computed(node) = name { + if node.contains_arguments() { + return true; + } + } + if pattern.contains_arguments() { + return true; + } + if let Some(init) = default_init { + if init.contains_arguments() { + return true; + } + } + } + _ => {} + } + false + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::SingleName { + default_init: Some(node), + .. + } => { + if node.contains(symbol) { + return true; + } + } + Self::AssignmentRestPropertyAccess { access, .. } => { + if access.target().contains(symbol) { + return true; + } + } + Self::Pattern { + pattern, + default_init, + .. + } => { + if let Some(node) = default_init { + if node.contains(symbol) { + return true; + } + } + if pattern.contains(symbol) { + return true; + } + } + _ => {} + } + false + } + + /// Gets the list of identifiers declared by the object binding pattern. + #[inline] + pub(crate) fn idents(&self) -> Vec { + match self { + Self::SingleName { ident, .. } | Self::RestProperty { ident, .. } => { + vec![*ident] + } + Self::AssignmentPropertyAccess { + name: PropertyName::Literal(lit), + .. + } => { + vec![*lit] + } + Self::Pattern { pattern, .. } => pattern.idents(), + _ => Vec::new(), + } + } +} + +impl ToInternedString for PatternObjectElement { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Empty => String::new(), + Self::SingleName { + ident, + name: property_name, + default_init, + } => { + let mut buf = match property_name { + PropertyName::Literal(name) if *name == *ident => { + format!(" {}", interner.resolve_expect(*ident)) + } + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + interner.resolve_expect(*ident) + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + interner.resolve_expect(*ident) + ) + } + }; + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::RestProperty { + ident: property_name, + excluded_keys: _, + } => { + format!(" ... {}", interner.resolve_expect(*property_name)) + } + Self::AssignmentRestPropertyAccess { access, .. } => { + format!(" ... {}", access.to_interned_string(interner)) + } + Self::AssignmentPropertyAccess { + name, + access, + default_init, + } => { + let mut buf = match name { + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + access.to_interned_string(interner) + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + access.to_interned_string(interner) + ) + } + }; + if let Some(init) = &default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::Pattern { + name, + pattern, + default_init, + } => { + let mut buf = match name { + PropertyName::Literal(name) => { + format!( + " {} : {}", + interner.resolve_expect(*name), + pattern.to_interned_string(interner), + ) + } + PropertyName::Computed(node) => { + format!( + " [{}] : {}", + node.to_interned_string(interner), + pattern.to_interned_string(interner), + ) + } + }; + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + } + } +} + +/// `PatternTypeArray` represents the different types of bindings that an array binding pattern may contain. +/// +/// More information: +/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - `ArrayBindingPattern`][spec1] +/// +/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum PatternArrayElement { + /// Empty represents an empty array pattern e.g. `[ ]`. + /// + /// This may occur because the `Elision` and `BindingRestElement` in the first type of + /// array binding pattern are both optional. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - ArrayBindingPattern][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern + Empty, + + /// Elision represents the elision of an item in the array binding pattern. + /// + /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions. + /// This variant strictly represents one elision. If there are multiple, this should be used multiple times. + /// + /// More information: + /// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-Elision + Elision, + + /// SingleName represents a `SingleName` with an identifier and an optional default initializer. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding + SingleName { + ident: Sym, + default_init: Option, + }, + + /// PropertyAccess represents a binding with a property accessor. + /// + /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// This is only used when a array literal is used as the left-hand-side of an assignment expression. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + PropertyAccess { access: PropertyAccess }, + + /// Pattern represents a `Pattern` in an `Element` of an array pattern. + /// + /// The pattern and the optional default initializer are both stored in the DeclarationPattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement + Pattern { + pattern: Pattern, + default_init: Option, + }, + + /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + SingleNameRest { ident: Sym }, + + /// PropertyAccess represents a rest (spread operator) with a property accessor. + /// + /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// This is only used when a array literal is used as the left-hand-side of an assignment expression. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression + PropertyAccessRest { access: PropertyAccess }, + + /// PatternRest represents a `Pattern` in a `RestElement` of an array pattern. + /// + /// More information: + /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] + /// + /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement + PatternRest { pattern: Pattern }, +} + +impl PatternArrayElement { + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Self::SingleName { + default_init: Some(init), + .. + } => { + if init.contains_arguments() { + return true; + } + } + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { + if access.target().contains_arguments() { + return true; + } + if let PropertyAccessField::Expr(expr) = access.field() { + if expr.contains_arguments() { + return true; + } + } + } + Self::PatternRest { pattern } => { + if pattern.contains_arguments() { + return true; + } + } + Self::Pattern { + pattern, + default_init, + } => { + if pattern.contains_arguments() { + return true; + } + if let Some(init) = default_init { + if init.contains_arguments() { + return true; + } + } + } + _ => {} + } + false + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::SingleName { + default_init: Some(node), + .. + } => { + if node.contains(symbol) { + return true; + } + } + Self::PropertyAccess { access } | Self::PropertyAccessRest { access } => { + if access.target().contains(symbol) { + return true; + } + + if let PropertyAccessField::Expr(expr) = access.field() { + if expr.contains(symbol) { + return true; + } + } + } + Self::Pattern { + pattern, + default_init, + } => { + if pattern.contains(symbol) { + return true; + } + + if let Some(init) = default_init { + if init.contains(symbol) { + return true; + } + } + } + Self::PatternRest { pattern } => { + if pattern.contains(symbol) { + return true; + } + } + _ => {} + } + false + } + + /// Gets the list of identifiers in the array pattern element. + #[inline] + pub(crate) fn idents(&self) -> Vec { + match self { + Self::SingleName { ident, .. } => { + vec![*ident] + } + Self::Pattern { pattern, .. } | Self::PatternRest { pattern } => pattern.idents(), + Self::SingleNameRest { ident } => vec![*ident], + _ => Vec::new(), + } + } +} + +impl ToInternedString for PatternArrayElement { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::Empty => String::new(), + Self::Elision => " ".to_owned(), + Self::SingleName { + ident, + default_init, + } => { + let mut buf = format!(" {}", interner.resolve_expect(*ident)); + if let Some(ref init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::PropertyAccess { access } => { + format!(" {}", access.to_interned_string(interner)) + } + Self::Pattern { + pattern, + default_init, + } => { + let mut buf = format!(" {}", pattern.to_interned_string(interner)); + if let Some(init) = default_init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } + Self::SingleNameRest { ident } => { + format!(" ... {}", interner.resolve_expect(*ident)) + } + Self::PropertyAccessRest { access } => { + format!(" ... {}", access.to_interned_string(interner)) + } + Self::PatternRest { pattern } => { + format!(" ... {}", pattern.to_interned_string(interner)) + } + } + } +} diff --git a/boa_engine/src/syntax/ast/node/object/mod.rs b/boa_engine/src/syntax/ast/property.rs similarity index 57% rename from boa_engine/src/syntax/ast/node/object/mod.rs rename to boa_engine/src/syntax/ast/property.rs index e9156dfde5b..f416b7ec043 100644 --- a/boa_engine/src/syntax/ast/node/object/mod.rs +++ b/boa_engine/src/syntax/ast/property.rs @@ -1,154 +1,13 @@ -//! Object node. -use crate::string::ToStringEscaped; -use crate::syntax::ast::{ - node::{ - declaration::block_to_string, join_nodes, AsyncFunctionExpr, AsyncGeneratorExpr, - FormalParameterList, FunctionExpr, GeneratorExpr, Node, StatementList, - }, - Const, -}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - -/// 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(Serialize, Deserialize))] -#[cfg_attr(feature = "deser", serde(transparent))] -#[derive(Clone, Debug, PartialEq)] -pub struct Object { - properties: Box<[PropertyDefinition]>, -} - -impl Object { - pub fn properties(&self) -> &[PropertyDefinition] { - &self.properties - } - - /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) 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)) - } - 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(method, key) => { - format!( - "{indentation}{}{}({}) {},\n", - match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", - }, - key.to_interned_string(interner), - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Generator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::AsyncGenerator(node) => { - join_nodes(interner, &node.parameters().parameters) - } - MethodDefinition::Async(node) => { - join_nodes(interner, &node.parameters().parameters) - } - }, - match &method { - MethodDefinition::Get(node) - | MethodDefinition::Set(node) - | MethodDefinition::Ordinary(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Generator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::AsyncGenerator(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - MethodDefinition::Async(node) => { - block_to_string(node.body(), interner, indent_n + 1) - } - }, - ) - } - PropertyDefinition::CoverInitializedName(ident, expr) => { - format!( - "{indentation}{} = {},\n", - interner.resolve_expect(*ident), - expr.to_no_indent_string(interner, indent_n + 1) - ) - } - }); - } - buf.push_str(&format!("{}}}", " ".repeat(indent_n))); - - buf - } -} - -impl ToInternedString for Object { - fn to_interned_string(&self, interner: &Interner) -> String { - self.to_indented_string(interner, 0) - } -} - -impl From for Object -where - T: Into>, -{ - fn from(props: T) -> Self { - Self { - properties: props.into(), - } - } -} +use crate::string::ToStringEscaped; -impl From for Node { - fn from(obj: Object) -> Self { - Self::Object(obj) - } -} +use super::{ + expression::literal::Literal, + function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, + statement::StatementList, + Expression, +}; /// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure. /// @@ -163,7 +22,7 @@ impl From for Node { /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum PropertyDefinition { /// Puts a variable into an object. @@ -184,7 +43,7 @@ pub enum PropertyDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions - Property(PropertyName, Node), + Property(PropertyName, Expression), /// A property of an object can also refer to a function or a getter or setter method. /// @@ -207,7 +66,7 @@ pub enum PropertyDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties - SpreadObject(Node), + SpreadObject(Expression), /// Cover grammar for when an object literal is used as an object biding pattern. /// @@ -215,39 +74,7 @@ pub enum PropertyDefinition { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName - CoverInitializedName(Sym, Node), -} - -impl PropertyDefinition { - /// Creates an `IdentifierReference` property definition. - pub fn identifier_reference(ident: Sym) -> Self { - Self::IdentifierReference(ident) - } - - /// Creates a `Property` definition. - pub fn property(name: N, value: V) -> Self - where - N: Into, - V: Into, - { - Self::Property(name.into(), value.into()) - } - - /// Creates a `MethodDefinition`. - pub fn method_definition(kind: MethodDefinition, name: N) -> Self - where - N: Into, - { - Self::MethodDefinition(kind, name.into()) - } - - /// Creates a `SpreadObject`. - pub fn spread_object(obj: O) -> Self - where - O: Into, - { - Self::SpreadObject(obj.into()) - } + CoverInitializedName(Sym, Expression), } /// Method definition. @@ -261,7 +88,7 @@ impl 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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", 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. @@ -279,7 +106,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get - Get(FunctionExpr), + Get(Function), /// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property. /// @@ -293,7 +120,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set - Set(FunctionExpr), + Set(Function), /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. /// @@ -303,7 +130,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax - Ordinary(FunctionExpr), + Ordinary(Function), /// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters. /// @@ -313,7 +140,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods - Generator(GeneratorExpr), + Generator(Generator), /// Async generators can be used to define a method /// @@ -323,7 +150,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_generator_methods - AsyncGenerator(AsyncGeneratorExpr), + AsyncGenerator(AsyncGenerator), /// Async function can be used to define a method /// @@ -333,7 +160,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_methods - Async(AsyncFunctionExpr), + Async(AsyncFunction), } impl MethodDefinition { @@ -368,7 +195,7 @@ impl MethodDefinition { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyName -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", 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. @@ -385,7 +212,7 @@ pub enum PropertyName { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-ComputedPropertyName - Computed(Node), + Computed(Expression), } impl PropertyName { @@ -398,10 +225,10 @@ impl PropertyName { } } - /// Returns the expression node if the property name is computed. - pub(crate) fn computed(&self) -> Option<&Node> { - if let Self::Computed(node) = self { - Some(node) + /// Returns the expression if the property name is computed. + pub(crate) fn computed(&self) -> Option<&Expression> { + if let Self::Computed(expr) = self { + Some(expr) } else { None } @@ -411,7 +238,7 @@ impl PropertyName { pub(in crate::syntax) fn prop_name(&self) -> Option { match self { PropertyName::Literal(sym) - | PropertyName::Computed(Node::Const(Const::String(sym))) => Some(*sym), + | PropertyName::Computed(Expression::Literal(Literal::String(sym))) => Some(*sym), PropertyName::Computed(_) => None, } } @@ -436,8 +263,8 @@ impl From for PropertyName { } } -impl From for PropertyName { - fn from(name: Node) -> Self { +impl From for PropertyName { + fn from(name: Expression) -> Self { Self::Computed(name) } } @@ -448,7 +275,7 @@ impl From for PropertyName { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-ClassElementName -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub(crate) enum ClassElementName { PropertyName(PropertyName), diff --git a/boa_engine/src/syntax/ast/punctuator.rs b/boa_engine/src/syntax/ast/punctuator.rs index d2a980117c9..05e90f5c3a8 100644 --- a/boa_engine/src/syntax/ast/punctuator.rs +++ b/boa_engine/src/syntax/ast/punctuator.rs @@ -5,7 +5,10 @@ //! //! [spec]: https://tc39.es/ecma262/#prod-Punctuator -use crate::syntax::ast::op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp}; +use crate::syntax::ast::expression::operator::{ + assign::op::AssignOp, + binary::op::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, +}; use std::{ convert::TryInto, fmt::{Display, Error, Formatter}, @@ -138,49 +141,59 @@ pub enum Punctuator { } impl Punctuator { + /// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator + /// + /// If there is no match, `None` will be returned. + pub const fn as_assign_op(self) -> Option { + match self { + Self::Assign => Some(AssignOp::Assign), + Self::AssignAdd => Some(AssignOp::Add), + Self::AssignAnd => Some(AssignOp::And), + Self::AssignBoolAnd => Some(AssignOp::BoolAnd), + Self::AssignBoolOr => Some(AssignOp::BoolOr), + Self::AssignCoalesce => Some(AssignOp::Coalesce), + Self::AssignDiv => Some(AssignOp::Div), + Self::AssignLeftSh => Some(AssignOp::Shl), + Self::AssignMod => Some(AssignOp::Mod), + Self::AssignMul => Some(AssignOp::Mul), + Self::AssignOr => Some(AssignOp::Or), + Self::AssignPow => Some(AssignOp::Exp), + Self::AssignRightSh => Some(AssignOp::Shr), + Self::AssignSub => Some(AssignOp::Sub), + Self::AssignURightSh => Some(AssignOp::Ushr), + Self::AssignXor => Some(AssignOp::Xor), + _ => None, + } + } + /// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator /// /// If there is no match, `None` will be returned. - pub const fn as_binop(self) -> Option { + pub const fn as_binary_op(self) -> Option { match self { - Self::AssignAdd => Some(BinOp::Assign(AssignOp::Add)), - Self::AssignAnd => Some(BinOp::Assign(AssignOp::And)), - Self::AssignBoolAnd => Some(BinOp::Assign(AssignOp::BoolAnd)), - Self::AssignBoolOr => Some(BinOp::Assign(AssignOp::BoolOr)), - Self::AssignCoalesce => Some(BinOp::Assign(AssignOp::Coalesce)), - Self::AssignDiv => Some(BinOp::Assign(AssignOp::Div)), - Self::AssignLeftSh => Some(BinOp::Assign(AssignOp::Shl)), - Self::AssignMod => Some(BinOp::Assign(AssignOp::Mod)), - Self::AssignMul => Some(BinOp::Assign(AssignOp::Mul)), - Self::AssignOr => Some(BinOp::Assign(AssignOp::Or)), - Self::AssignPow => Some(BinOp::Assign(AssignOp::Exp)), - Self::AssignRightSh => Some(BinOp::Assign(AssignOp::Shr)), - Self::AssignSub => Some(BinOp::Assign(AssignOp::Sub)), - Self::AssignURightSh => Some(BinOp::Assign(AssignOp::Ushr)), - Self::AssignXor => Some(BinOp::Assign(AssignOp::Xor)), - Self::Add => Some(BinOp::Num(NumOp::Add)), - Self::Sub => Some(BinOp::Num(NumOp::Sub)), - Self::Mul => Some(BinOp::Num(NumOp::Mul)), - Self::Div => Some(BinOp::Num(NumOp::Div)), - Self::Mod => Some(BinOp::Num(NumOp::Mod)), - Self::And => Some(BinOp::Bit(BitOp::And)), - Self::Or => Some(BinOp::Bit(BitOp::Or)), - Self::Xor => Some(BinOp::Bit(BitOp::Xor)), - Self::BoolAnd => Some(BinOp::Log(LogOp::And)), - Self::BoolOr => Some(BinOp::Log(LogOp::Or)), - Self::Coalesce => Some(BinOp::Log(LogOp::Coalesce)), - Self::Eq => Some(BinOp::Comp(CompOp::Equal)), - Self::NotEq => Some(BinOp::Comp(CompOp::NotEqual)), - Self::StrictEq => Some(BinOp::Comp(CompOp::StrictEqual)), - Self::StrictNotEq => Some(BinOp::Comp(CompOp::StrictNotEqual)), - Self::LessThan => Some(BinOp::Comp(CompOp::LessThan)), - Self::GreaterThan => Some(BinOp::Comp(CompOp::GreaterThan)), - Self::GreaterThanOrEq => Some(BinOp::Comp(CompOp::GreaterThanOrEqual)), - Self::LessThanOrEq => Some(BinOp::Comp(CompOp::LessThanOrEqual)), - Self::LeftSh => Some(BinOp::Bit(BitOp::Shl)), - Self::RightSh => Some(BinOp::Bit(BitOp::Shr)), - Self::URightSh => Some(BinOp::Bit(BitOp::UShr)), - Self::Comma => Some(BinOp::Comma), + Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)), + Self::Sub => Some(BinaryOp::Arithmetic(ArithmeticOp::Sub)), + Self::Mul => Some(BinaryOp::Arithmetic(ArithmeticOp::Mul)), + Self::Div => Some(BinaryOp::Arithmetic(ArithmeticOp::Div)), + Self::Mod => Some(BinaryOp::Arithmetic(ArithmeticOp::Mod)), + Self::And => Some(BinaryOp::Bitwise(BitwiseOp::And)), + Self::Or => Some(BinaryOp::Bitwise(BitwiseOp::Or)), + Self::Xor => Some(BinaryOp::Bitwise(BitwiseOp::Xor)), + Self::BoolAnd => Some(BinaryOp::Logical(LogicalOp::And)), + Self::BoolOr => Some(BinaryOp::Logical(LogicalOp::Or)), + Self::Coalesce => Some(BinaryOp::Logical(LogicalOp::Coalesce)), + Self::Eq => Some(BinaryOp::Relational(RelationalOp::Equal)), + Self::NotEq => Some(BinaryOp::Relational(RelationalOp::NotEqual)), + Self::StrictEq => Some(BinaryOp::Relational(RelationalOp::StrictEqual)), + Self::StrictNotEq => Some(BinaryOp::Relational(RelationalOp::StrictNotEqual)), + Self::LessThan => Some(BinaryOp::Relational(RelationalOp::LessThan)), + Self::GreaterThan => Some(BinaryOp::Relational(RelationalOp::GreaterThan)), + Self::GreaterThanOrEq => Some(BinaryOp::Relational(RelationalOp::GreaterThanOrEqual)), + Self::LessThanOrEq => Some(BinaryOp::Relational(RelationalOp::LessThanOrEqual)), + Self::LeftSh => Some(BinaryOp::Bitwise(BitwiseOp::Shl)), + Self::RightSh => Some(BinaryOp::Bitwise(BitwiseOp::Shr)), + Self::URightSh => Some(BinaryOp::Bitwise(BitwiseOp::UShr)), + Self::Comma => Some(BinaryOp::Comma), _ => None, } } @@ -248,10 +261,10 @@ impl Punctuator { } } -impl TryInto for Punctuator { +impl TryInto for Punctuator { type Error = String; - fn try_into(self) -> Result { - self.as_binop() + fn try_into(self) -> Result { + self.as_binary_op() .ok_or_else(|| format!("No binary operation for {self}")) } } diff --git a/boa_engine/src/syntax/ast/node/block/mod.rs b/boa_engine/src/syntax/ast/statement/block.rs similarity index 71% rename from boa_engine/src/syntax/ast/node/block/mod.rs rename to boa_engine/src/syntax/ast/statement/block.rs index f6495006f38..be70289f457 100644 --- a/boa_engine/src/syntax/ast/node/block/mod.rs +++ b/boa_engine/src/syntax/ast/statement/block.rs @@ -1,14 +1,8 @@ //! Block AST node. -use super::{Node, StatementList}; +use super::{Statement, StatementList}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - /// A `block` statement (or compound statement in other languages) is used to group zero or /// more statements. /// @@ -24,8 +18,8 @@ mod tests; /// /// [spec]: https://tc39.es/ecma262/#prod-BlockStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq, Default)] pub struct Block { #[cfg_attr(feature = "deser", serde(flatten))] statements: StatementList, @@ -34,8 +28,8 @@ pub struct Block { impl Block { /// Gets the list of statements and declarations in this block. - pub(crate) fn items(&self) -> &[Node] { - self.statements.items() + pub(crate) fn statements(&self) -> &[Statement] { + self.statements.statements() } /// Get the lexically declared names of the block. @@ -80,8 +74,34 @@ impl ToInternedString for Block { } } -impl From for Node { +impl From for Statement { fn from(block: Block) -> Self { Self::Block(block) } } + +#[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/declaration.rs b/boa_engine/src/syntax/ast/statement/declaration.rs new file mode 100644 index 00000000000..f4e5573dc75 --- /dev/null +++ b/boa_engine/src/syntax/ast/statement/declaration.rs @@ -0,0 +1,313 @@ +//! Declaration nodes +use crate::syntax::ast::{ + expression::{Expression, Identifier}, + join_nodes, + pattern::Pattern, + statement::Statement, + ContainsSymbol, +}; +use boa_interner::{Interner, Sym, ToInternedString}; + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum DeclarationList { + /// The `const` statements are block-scoped, much like variables defined using the `let` + /// keyword. + /// + /// This declaration creates a constant whose scope can be either global or local to the block + /// in which it is declared. Global constants do not become properties of the window object, + /// unlike var variables. + /// + /// An initializer for a constant is required. You must specify its value in the same statement + /// in which it's declared. (This makes sense, given that it can't be changed later.) + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const + /// [identifier]: https://developer.mozilla.org/en-US/docs/Glossary/identifier + /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions + Const(Box<[Declaration]>), + + /// The `let` statement declares a block scope local variable, optionally initializing it to a + /// value. + /// + /// + /// `let` allows you to declare variables that are limited to a scope of a block statement, or + /// expression on which it is used, unlike the `var` keyword, which defines a variable + /// globally, or locally to an entire function regardless of block scope. + /// + /// Just like const the `let` does not create properties of the window object when declared + /// globally (in the top-most scope). + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let + Let(Box<[Declaration]>), + + /// The `var` statement declares a variable, optionally initializing it to a value. + /// + /// var declarations, wherever they occur, are processed before any code is executed. This is + /// called hoisting, and is discussed further below. + /// + /// The scope of a variable declared with var is its current execution context, which is either + /// the enclosing function or, for variables declared outside any function, global. If you + /// re-declare a JavaScript variable, it will not lose its value. + /// + /// Assigning a value to an undeclared variable implicitly creates it as a global variable (it + /// becomes a property of the global object) when the assignment is executed. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-VariableStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var + Var(Box<[Declaration]>), +} + +impl DeclarationList { + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.as_ref().iter().any(|decl| decl.contains(symbol)) + } + + pub(crate) fn contains_arguments(&self) -> bool { + self.as_ref().iter().any(Declaration::contains_arguments) + } +} + +impl AsRef<[Declaration]> for DeclarationList { + fn as_ref(&self) -> &[Declaration] { + use DeclarationList::{Const, Let, Var}; + match self { + Var(list) | Const(list) | Let(list) => list, + } + } +} + +impl ToInternedString for DeclarationList { + fn to_interned_string(&self, interner: &Interner) -> String { + if self.as_ref().is_empty() { + String::new() + } else { + use DeclarationList::{Const, Let, Var}; + format!( + "{} {}", + match &self { + Let(_) => "let", + Const(_) => "const", + Var(_) => "var", + }, + join_nodes(interner, self.as_ref()) + ) + } + } +} + +impl From for Statement { + fn from(list: DeclarationList) -> Self { + Statement::DeclarationList(list) + } +} + +impl From for Box<[Declaration]> { + fn from(d: Declaration) -> Self { + Box::new([d]) + } +} + +/// Declaration represents a variable declaration of some kind. +/// +/// For `let` and `const` declarations this type represents a [`LexicalBinding`][spec1] +/// +/// For `var` declarations this type represents a [`VariableDeclaration`][spec2] +/// +/// More information: +/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct Declaration { + binding: Binding, + init: Option, +} + +impl ToInternedString for Declaration { + fn to_interned_string(&self, interner: &Interner) -> String { + let mut buf = self.binding.to_interned_string(interner); + + if let Some(ref init) = self.init { + buf.push_str(&format!(" = {}", init.to_interned_string(interner))); + } + buf + } +} + +impl Declaration { + /// Creates a new variable declaration from a `BindingIdentifier`. + #[inline] + pub(in crate::syntax) fn from_identifier(ident: Identifier, init: Option) -> Self { + Self { + binding: Binding::Identifier(ident), + init, + } + } + + /// Creates a new variable declaration from a `Pattern`. + #[inline] + pub(in crate::syntax) 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 { + &self.binding + } + + /// Gets the initialization expression for the variable declaration, if any. + #[inline] + pub(crate) fn init(&self) -> Option<&Expression> { + self.init.as_ref() + } + + pub(crate) fn contains_arguments(&self) -> bool { + if let Some(ref node) = self.init { + if node.contains_arguments() { + return true; + } + } + self.binding.contains_arguments() + } + + /// Returns `true` if the variable declaration contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + if let Some(ref node) = self.init { + if node.contains(symbol) { + return true; + } + } + self.binding.contains(symbol) + } + + /// Gets the list of declared identifiers. + pub(crate) fn idents(&self) -> Vec { + self.binding.idents() + } +} + +/// Binding represents either an individual binding or a binding pattern. +/// +/// More information: +/// - [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))] +#[derive(Clone, Debug, PartialEq)] +pub enum Binding { + Identifier(Identifier), + Pattern(Pattern), +} + +impl From for Binding { + fn from(id: Identifier) -> Self { + Self::Identifier(id) + } +} + +impl From for Binding { + fn from(pat: Pattern) -> Self { + Self::Pattern(pat) + } +} + +impl Binding { + pub(crate) fn contains_arguments(&self) -> bool { + matches!(self, Binding::Pattern(ref pattern) if pattern.contains_arguments()) + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + matches!(self, Binding::Pattern(ref pattern) if pattern.contains(symbol)) + } + + /// Gets the list of declared identifiers. + pub(crate) fn idents(&self) -> Vec { + match self { + Binding::Identifier(id) => vec![id.sym()], + Binding::Pattern(ref pat) => pat.idents(), + } + } +} + +impl ToInternedString for Binding { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Binding::Identifier(id) => id.to_interned_string(interner), + Binding::Pattern(ref pattern) => pattern.to_interned_string(interner), + } + } +} + +#[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/node/conditional/if_node/mod.rs b/boa_engine/src/syntax/ast/statement/if.rs similarity index 65% rename from boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs rename to boa_engine/src/syntax/ast/statement/if.rs index 618271a6b16..3dfc9a13900 100644 --- a/boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs +++ b/boa_engine/src/syntax/ast/statement/if.rs @@ -1,8 +1,7 @@ -use crate::syntax::ast::node::Node; -use boa_interner::{Interner, ToInternedString}; +//! If statement -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use crate::syntax::ast::{expression::Expression, statement::Statement}; +use boa_interner::{Interner, ToInternedString}; /// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If /// the condition is [`falsy`][falsy], another statement can be executed. @@ -20,47 +19,37 @@ use serde::{Deserialize, Serialize}; /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct If { - cond: Box, - body: Box, - else_node: Option>, + condition: Expression, + body: Box, + else_node: Option>, } impl If { - pub fn cond(&self) -> &Node { - &self.cond + pub fn cond(&self) -> &Expression { + &self.condition } - pub fn body(&self) -> &Node { + pub fn body(&self) -> &Statement { &self.body } - pub fn else_node(&self) -> Option<&Node> { + pub fn else_node(&self) -> Option<&Statement> { self.else_node.as_ref().map(Box::as_ref) } /// Creates an `If` AST node. - pub fn new(condition: C, body: B, else_node: OE) -> Self - where - C: Into, - B: Into, - E: Into, - OE: Into>, - { + pub fn new(condition: Expression, body: Statement, else_node: Option) -> Self { Self { - cond: Box::new(condition.into()), - body: Box::new(body.into()), - else_node: else_node.into().map(E::into).map(Box::new), + condition, + body: body.into(), + else_node: else_node.map(Box::new), } } - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indent: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indent: usize) -> String { let mut buf = format!("if ({}) ", self.cond().to_interned_string(interner)); match self.else_node() { Some(else_e) => { @@ -84,8 +73,25 @@ impl ToInternedString for If { } } -impl From for Node { +impl From for Statement { fn from(if_stm: If) -> Self { Self::If(if_stm) } } + +#[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/node/iteration/break_node/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/break.rs similarity index 61% rename from boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/break.rs index 28731b9fe3d..995a60f921d 100644 --- a/boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/break.rs @@ -1,12 +1,6 @@ -use crate::syntax::ast::Node; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - +use crate::syntax::ast::Statement; /// The `break` statement terminates the current loop, switch, or label statement and transfers /// program control to the statement following the terminated statement. /// @@ -21,7 +15,7 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Break { label: Option, @@ -29,13 +23,8 @@ pub struct Break { impl Break { /// Creates a `Break` AST node. - pub fn new(label: L) -> Self - where - L: Into>, - { - Self { - label: label.into(), - } + pub fn new(label: Option) -> Self { + Self { label } } /// Gets the label of the break statement, if any. @@ -54,8 +43,42 @@ impl ToInternedString for Break { } } -impl From for Node { +impl From for Statement { fn from(break_smt: Break) -> Self { Self::Break(break_smt) } } + +#[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/node/iteration/continue_node/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/continue.rs similarity index 80% rename from boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/continue.rs index dc3b2498f4e..5718c5f495d 100644 --- a/boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/continue.rs @@ -1,9 +1,5 @@ -use crate::syntax::ast::node::Node; +use crate::syntax::ast::statement::Statement; use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - /// The `continue` statement terminates execution of the statements in the current iteration of /// the current or labeled loop, and continues execution of the loop with the next iteration. /// @@ -17,7 +13,7 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Continue { label: Option, @@ -25,13 +21,8 @@ pub struct Continue { impl Continue { /// Creates a `Continue` AST node. - pub fn new(label: L) -> Self - where - L: Into>, - { - Self { - label: label.into(), - } + pub fn new(label: Option) -> Self { + Self { label } } pub fn label(&self) -> Option { @@ -49,7 +40,7 @@ impl ToInternedString for Continue { } } -impl From for Node { +impl From for Statement { fn from(cont: Continue) -> Self { Self::Continue(cont) } diff --git a/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs similarity index 71% rename from boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs index 4f0f93bd4e3..db8229e17b7 100644 --- a/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs @@ -1,9 +1,6 @@ -use crate::syntax::ast::node::Node; +use crate::syntax::ast::{expression::Expression, statement::Statement}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - /// The `do...while` statement creates a loop that executes a specified statement until the /// test condition evaluates to false. /// @@ -16,21 +13,21 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct DoWhileLoop { - body: Box, - cond: Box, + body: Box, + condition: Expression, label: Option, } impl DoWhileLoop { - pub fn body(&self) -> &Node { + pub fn body(&self) -> &Statement { &self.body } - pub fn cond(&self) -> &Node { - &self.cond + pub fn cond(&self) -> &Expression { + &self.condition } pub fn label(&self) -> Option { @@ -42,24 +39,16 @@ impl DoWhileLoop { } /// Creates a `DoWhileLoop` AST node. - pub fn new(body: B, condition: C) -> Self - where - B: Into, - C: Into, - { + pub fn new(body: Statement, condition: Expression) -> Self { Self { - body: Box::new(body.into()), - cond: Box::new(condition.into()), + body: body.into(), + condition, label: None, } } /// Converts the "do while" loop to a string with the given indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = if let Some(label) = self.label { format!("{}: ", interner.resolve_expect(label)) } else { @@ -81,7 +70,7 @@ impl ToInternedString for DoWhileLoop { } } -impl From for Node { +impl From for Statement { fn from(do_while: DoWhileLoop) -> Self { Self::DoWhileLoop(do_while) } diff --git a/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs similarity index 61% rename from boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs index b3b6194f390..ebf234e1153 100644 --- a/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs @@ -1,28 +1,23 @@ -use crate::syntax::ast::node::{iteration::IterableLoopInitializer, Node}; +use crate::syntax::ast::{ + expression::Expression, + statement::{iteration::IterableLoopInitializer, Statement}, +}; use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ForInLoop { - init: Box, - expr: Box, - body: Box, + init: IterableLoopInitializer, + expr: Expression, + body: Box, label: Option, } impl ForInLoop { - pub fn new(init: IterableLoopInitializer, expr: I, body: B) -> Self - where - I: Into, - B: Into, - { + pub fn new(init: IterableLoopInitializer, expr: Expression, body: Statement) -> Self { Self { - init: Box::new(init), - expr: Box::new(expr.into()), - body: Box::new(body.into()), + init, + expr, + body: body.into(), label: None, } } @@ -31,11 +26,11 @@ impl ForInLoop { &self.init } - pub fn expr(&self) -> &Node { + pub fn expr(&self) -> &Expression { &self.expr } - pub fn body(&self) -> &Node { + pub fn body(&self) -> &Statement { &self.body } @@ -48,11 +43,7 @@ impl ForInLoop { } /// Converts the "for in" loop to a string with the given indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = if let Some(label) = self.label { format!("{}: ", interner.resolve_expect(label)) } else { @@ -75,7 +66,7 @@ impl ToInternedString for ForInLoop { } } -impl From for Node { +impl From for Statement { fn from(for_in: ForInLoop) -> Self { Self::ForInLoop(for_in) } diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs new file mode 100644 index 00000000000..5d59dea1435 --- /dev/null +++ b/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs @@ -0,0 +1,215 @@ +use crate::syntax::ast::{ + statement::{ + declaration::{Declaration, DeclarationList}, + Statement, + }, + ContainsSymbol, Expression, +}; +use boa_interner::{Interner, Sym, ToInternedString}; + +/// The `for` statement creates a loop that consists of three optional expressions. +/// +/// A `for` loop repeats until a specified condition evaluates to `false`. +/// The JavaScript for loop is similar to the Java and C for loop. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [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))] +#[derive(Clone, Debug, PartialEq)] +pub struct ForLoop { + #[cfg_attr(feature = "deser", serde(flatten))] + inner: Box, + label: Option, +} + +impl ForLoop { + /// Creates a new for loop AST node. + pub(in crate::syntax) fn new( + init: Option, + condition: Option, + final_expr: Option, + body: Statement, + ) -> Self { + Self { + inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)), + label: None, + } + } + + /// Gets the initialization node. + pub fn init(&self) -> Option<&ForLoopInitializer> { + self.inner.init() + } + + /// Gets the loop condition node. + pub fn condition(&self) -> Option<&Expression> { + self.inner.condition() + } + + /// Gets the final expression node. + pub fn final_expr(&self) -> Option<&Expression> { + self.inner.final_expr() + } + + /// Gets the body of the for loop. + pub fn body(&self) -> &Statement { + self.inner.body() + } + + /// Converts the for loop to a string with the given indentation. + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = if let Some(label) = self.label { + format!("{}: ", interner.resolve_expect(label)) + } else { + String::new() + }; + buf.push_str("for ("); + if let Some(init) = self.init() { + buf.push_str(&init.to_interned_string(interner)); + } + buf.push_str("; "); + if let Some(condition) = self.condition() { + buf.push_str(&condition.to_interned_string(interner)); + } + buf.push_str("; "); + if let Some(final_expr) = self.final_expr() { + buf.push_str(&final_expr.to_interned_string(interner)); + } + buf.push_str(&format!( + ") {}", + self.inner.body().to_indented_string(interner, indentation) + )); + + buf + } + + pub fn label(&self) -> Option { + self.label + } + + pub fn set_label(&mut self, label: Sym) { + self.label = Some(label); + } +} + +impl ToInternedString for ForLoop { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} + +impl From for Statement { + fn from(for_loop: ForLoop) -> Self { + Self::ForLoop(for_loop) + } +} + +/// Inner structure to avoid multiple indirections in the heap. +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +struct InnerForLoop { + init: Option, + condition: Option, + final_expr: Option, + body: Statement, +} + +impl InnerForLoop { + /// Creates a new inner for loop. + fn new( + init: Option, + condition: Option, + final_expr: Option, + body: Statement, + ) -> Self { + Self { + init, + condition, + final_expr, + body, + } + } + + /// Gets the initialization node. + fn init(&self) -> Option<&ForLoopInitializer> { + self.init.as_ref() + } + + /// Gets the loop condition node. + fn condition(&self) -> Option<&Expression> { + self.condition.as_ref() + } + + /// Gets the final expression node. + fn final_expr(&self) -> Option<&Expression> { + self.final_expr.as_ref() + } + + /// Gets the body of the for loop. + fn body(&self) -> &Statement { + &self.body + } +} + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum ForLoopInitializer { + Expression(Expression), + DeclarationList(DeclarationList), +} + +impl ForLoopInitializer { + /// Return the bound names of a for loop initializer. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames + pub(crate) fn bound_names(&self) -> Vec { + match self { + Self::DeclarationList(DeclarationList::Let(list) | DeclarationList::Const(list)) => { + list.iter().flat_map(Declaration::idents).collect() + } + _ => Vec::new(), + } + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::DeclarationList(list) => list.contains(symbol), + Self::Expression(expr) => expr.contains(symbol), + } + } + + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Self::DeclarationList(list) => list.contains_arguments(), + Self::Expression(expr) => expr.contains_arguments(), + } + } +} + +impl ToInternedString for ForLoopInitializer { + fn to_interned_string(&self, interner: &Interner) -> String { + match self { + Self::DeclarationList(list) => list.to_interned_string(interner), + Self::Expression(expr) => expr.to_interned_string(interner), + } + } +} + +impl From for ForLoopInitializer { + fn from(expr: Expression) -> Self { + ForLoopInitializer::Expression(expr) + } +} + +impl From for ForLoopInitializer { + fn from(list: DeclarationList) -> Self { + ForLoopInitializer::DeclarationList(list) + } +} diff --git a/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs similarity index 69% rename from boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs index 18df63fe5dc..3cef364d8da 100644 --- a/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs @@ -1,4 +1,7 @@ -use crate::syntax::ast::node::{iteration::IterableLoopInitializer, Node}; +use crate::syntax::ast::{ + expression::Expression, + statement::{iteration::IterableLoopInitializer, Statement}, +}; use boa_interner::{Interner, Sym, ToInternedString}; #[cfg(feature = "deser")] @@ -7,24 +10,25 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ForOfLoop { - init: Box, - iterable: Box, - body: Box, + init: IterableLoopInitializer, + iterable: Expression, + body: Box, label: Option, r#await: bool, } impl ForOfLoop { /// Creates a new "for of" loop AST node. - pub fn new(init: IterableLoopInitializer, iterable: I, body: B, r#await: bool) -> Self - where - I: Into, - B: Into, - { + pub fn new( + init: IterableLoopInitializer, + iterable: Expression, + body: Statement, + r#await: bool, + ) -> Self { Self { - init: Box::new(init), - iterable: Box::new(iterable.into()), - body: Box::new(body.into()), + init, + iterable, + body: body.into(), label: None, r#await, } @@ -34,11 +38,11 @@ impl ForOfLoop { &self.init } - pub fn iterable(&self) -> &Node { + pub fn iterable(&self) -> &Expression { &self.iterable } - pub fn body(&self) -> &Node { + pub fn body(&self) -> &Statement { &self.body } @@ -56,11 +60,7 @@ impl ForOfLoop { } /// Converts the "for of" loop to a string with the given indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = if let Some(label) = self.label { format!("{}: ", interner.resolve_expect(label)) } else { @@ -83,7 +83,7 @@ impl ToInternedString for ForOfLoop { } } -impl From for Node { +impl From for Statement { fn from(for_of: ForOfLoop) -> Self { Self::ForOfLoop(for_of) } diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/mod.rs new file mode 100644 index 00000000000..27d57d1e8a2 --- /dev/null +++ b/boa_engine/src/syntax/ast/statement/iteration/mod.rs @@ -0,0 +1,78 @@ +//! Iteration nodes + +pub mod r#break; +pub mod r#continue; +pub mod do_while_loop; +pub mod for_in_loop; +pub mod for_loop; +pub mod for_of_loop; +pub mod while_loop; + +use crate::syntax::ast::expression::Identifier; + +pub use self::{ + do_while_loop::DoWhileLoop, for_in_loop::ForInLoop, for_loop::ForLoop, for_of_loop::ForOfLoop, + r#break::Break, r#continue::Continue, while_loop::WhileLoop, +}; +use boa_interner::{Interner, Sym, ToInternedString}; + +use super::{declaration::Binding, ContainsSymbol}; + +#[cfg(test)] +mod tests; + +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum IterableLoopInitializer { + // TODO: This should also accept property accessors + Identifier(Identifier), + Var(Binding), + Let(Binding), + Const(Binding), +} + +impl IterableLoopInitializer { + /// Return the bound names of a for loop initializer. + /// + /// The returned list may contain duplicates. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames + pub(crate) fn bound_names(&self) -> Vec { + match self { + Self::Let(binding) | Self::Const(binding) => binding.idents(), + _ => Vec::new(), + } + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::Var(declaration) | Self::Let(declaration) | Self::Const(declaration) => { + declaration.contains(symbol) + } + Self::Identifier(_) => false, + } + } + + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Self::Identifier(ident) => ident.sym() == Sym::ARGUMENTS, + Self::Var(bind) | Self::Let(bind) | Self::Const(bind) => bind.contains_arguments(), + } + } +} + +impl ToInternedString for IterableLoopInitializer { + fn to_interned_string(&self, interner: &Interner) -> String { + let (binding, pre) = match self { + Self::Identifier(ident) => return ident.to_interned_string(interner), + Self::Var(binding) => (binding, "var"), + Self::Let(binding) => (binding, "let"), + Self::Const(binding) => (binding, "const"), + }; + + format!("{pre} {}", binding.to_interned_string(interner)) + } +} diff --git a/boa_engine/src/syntax/ast/node/iteration/tests.rs b/boa_engine/src/syntax/ast/statement/iteration/tests.rs similarity index 97% rename from boa_engine/src/syntax/ast/node/iteration/tests.rs rename to boa_engine/src/syntax/ast/statement/iteration/tests.rs index b0e308b5ebd..98859f6a9b8 100644 --- a/boa_engine/src/syntax/ast/node/iteration/tests.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/tests.rs @@ -1,4 +1,4 @@ -use crate::{check_output, exec, TestAction}; +use crate::{check_output, exec, syntax::ast::test_formatting, TestAction}; #[test] fn while_loop_late_break() { @@ -487,7 +487,7 @@ fn for_in_continue_label() { #[test] fn fmt() { // Labeled and unlabeled for in loops - super::super::test_formatting( + test_formatting( r#" var str = ""; outer: for (let i in [1, 2]) { @@ -496,14 +496,14 @@ fn fmt() { continue outer; } str = str + b; - }; + } str = str + i; - }; + } str; "#, ); // Labeled and unlabeled for loops - super::super::test_formatting( + test_formatting( r#" var str = ""; outer: for (let i = 0; i < 10; ++i) { @@ -512,29 +512,29 @@ fn fmt() { continue outer; } str = str + j; - }; + } str = str + i; - }; + } str; "#, ); // Labeled and unlabeled for of loops - super::super::test_formatting( + 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 - super::super::test_formatting( + test_formatting( r#" do { break; @@ -545,7 +545,7 @@ fn fmt() { "#, ); // Labeled and unlabeled while loops - super::super::test_formatting( + test_formatting( r#" while (true) { break; diff --git a/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs similarity index 68% rename from boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs rename to boa_engine/src/syntax/ast/statement/iteration/while_loop.rs index 8fef373c3fc..b7efe78271d 100644 --- a/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs @@ -1,9 +1,5 @@ -use crate::syntax::ast::node::Node; +use crate::syntax::ast::{expression::Expression, statement::Statement}; use boa_interner::{Interner, Sym, ToInternedString}; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - /// The `while` statement creates a loop that executes a specified statement as long as the /// test condition evaluates to `true`. /// @@ -15,20 +11,20 @@ use serde::{Deserialize, Serialize}; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct WhileLoop { - cond: Box, - body: Box, + condition: Expression, + body: Box, label: Option, } impl WhileLoop { - pub fn cond(&self) -> &Node { - &self.cond + pub fn condition(&self) -> &Expression { + &self.condition } - pub fn body(&self) -> &Node { + pub fn body(&self) -> &Statement { &self.body } @@ -41,24 +37,16 @@ impl WhileLoop { } /// Creates a `WhileLoop` AST node. - pub fn new(condition: C, body: B) -> Self - where - C: Into, - B: Into, - { + pub fn new(condition: Expression, body: Statement) -> Self { Self { - cond: Box::new(condition.into()), - body: Box::new(body.into()), + condition, + body: body.into(), label: None, } } /// Converts the while loop to a string with the given indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = if let Some(label) = self.label { format!("{}: ", interner.resolve_expect(label)) } else { @@ -66,7 +54,7 @@ impl WhileLoop { }; buf.push_str(&format!( "while ({}) {}", - self.cond().to_interned_string(interner), + self.condition().to_interned_string(interner), self.body().to_indented_string(interner, indentation) )); @@ -80,7 +68,7 @@ impl ToInternedString for WhileLoop { } } -impl From for Node { +impl From for Statement { fn from(while_loop: WhileLoop) -> Self { Self::WhileLoop(while_loop) } diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs new file mode 100644 index 00000000000..02d8a6d187a --- /dev/null +++ b/boa_engine/src/syntax/ast/statement/mod.rs @@ -0,0 +1,393 @@ +//! This module implements the `Node` structure, which composes the AST. + +mod block; +mod r#if; +mod r#return; +mod throw; + +pub mod declaration; +pub mod iteration; +pub mod statement_list; +pub mod switch; +pub mod r#try; + +pub use self::{ + block::Block, + iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop}, + r#if::If, + r#return::Return, + r#try::{Catch, Finally, Try}, + statement_list::StatementList, + switch::{Case, Switch}, + throw::Throw, +}; +use self::{ + declaration::{Binding, Declaration, DeclarationList}, + iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, +}; + +use boa_interner::{Interner, Sym, ToInternedString}; +use rustc_hash::FxHashSet; +use std::cmp::Ordering; + +use super::{ + expression::Expression, + function::{AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator}, + ContainsSymbol, +}; + +// TODO: This should be split into Expression and Statement. +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + /// See [`Block`]. + Block(Block), + + /// See [`DeclarationList`] + DeclarationList(DeclarationList), + + /// A empty node. + /// + /// Empty statement do nothing, just return undefined. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty + Empty, + + /// See [`Expression`]. + Expression(Expression), + + /// See [`If`]. + If(If), + + /// See [`DoWhileLoop`]. + DoWhileLoop(DoWhileLoop), + + /// See [`WhileLoop`]. + WhileLoop(WhileLoop), + + /// See [`ForLoop`]. + ForLoop(ForLoop), + + /// See [`ForInLoop`]. + ForInLoop(ForInLoop), + + /// See [`ForOfLoop`]. + ForOfLoop(ForOfLoop), + + /// See[`Switch`]. + Switch(Switch), + + /// See [`Continue`]. + Continue(Continue), + + /// See [`Break`]. + Break(Break), + + /// See [`Return`]. + Return(Return), + + // TODO: Possibly add `with` statements. + + // TODO: extract labels into a `LabelledStatement` + /// See [`Throw`]. + Throw(Throw), + + /// See [`Try`]. + Try(Try), + + /// See [`Function`] + Function(Function), + + /// See [`Generator`] + Generator(Generator), + + /// See [`AsyncFunction`] + AsyncFunction(AsyncFunction), + + /// See [`AsyncGenerator`] + AsyncGenerator(AsyncGenerator), + + /// See [`Class`] + Class(Class), +} + +impl Statement { + /// Returns a node ordering based on the hoistability of each statement. + pub(crate) fn hoistable_order(a: &Self, b: &Self) -> Ordering { + match (a, b) { + (Statement::Function(_), Statement::Function(_)) => Ordering::Equal, + (_, Statement::Function(_)) => Ordering::Greater, + (Statement::Function(_), _) => Ordering::Less, + + (_, _) => Ordering::Equal, + } + } + + /// Creates a string of the value of the node with the given indentation. For example, an + /// indent level of 2 would produce this: + /// + /// ```js + /// function hello() { + /// console.log("hello"); + /// } + /// hello(); + /// a = 2; + /// ``` + fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { + let mut buf = match *self { + Self::Block(_) => String::new(), + _ => " ".repeat(indentation), + }; + + buf.push_str(&self.to_no_indent_string(interner, indentation)); + + buf + } + + /// Implements the display formatting with indentation. + /// + /// 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). + fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String { + match *self { + Self::Block(ref block) => block.to_indented_string(interner, indentation), + Self::DeclarationList(ref list) => list.to_interned_string(interner), + Self::Empty => ";".to_owned(), + Self::Expression(ref expr) => expr.to_indented_string(interner, indentation), + Self::If(ref if_smt) => if_smt.to_indented_string(interner, indentation), + Self::DoWhileLoop(ref do_while) => do_while.to_indented_string(interner, indentation), + Self::WhileLoop(ref while_loop) => while_loop.to_indented_string(interner, indentation), + Self::ForLoop(ref for_loop) => for_loop.to_indented_string(interner, indentation), + Self::ForInLoop(ref for_in) => for_in.to_indented_string(interner, indentation), + Self::ForOfLoop(ref for_of) => for_of.to_indented_string(interner, indentation), + Self::Switch(ref switch) => switch.to_indented_string(interner, indentation), + Self::Continue(ref cont) => cont.to_interned_string(interner), + Self::Break(ref break_smt) => break_smt.to_interned_string(interner), + Self::Return(ref ret) => ret.to_interned_string(interner), + Self::Throw(ref throw) => throw.to_interned_string(interner), + Self::Try(ref try_catch) => try_catch.to_indented_string(interner, indentation), + Self::Function(ref decl) => decl.to_indented_string(interner, indentation), + Self::Generator(ref decl) => decl.to_interned_string(interner), + Self::AsyncFunction(ref decl) => decl.to_indented_string(interner, indentation), + Self::AsyncGenerator(ref decl) => decl.to_indented_string(interner, indentation), + Self::Class(ref decl) => decl.to_indented_string(interner, indentation), + } + } + + pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + match self { + Statement::Block(block) => { + for node in block.statements() { + node.var_declared_names(vars); + } + } + Statement::If(if_statement) => { + if_statement.body().var_declared_names(vars); + if let Some(node) = if_statement.else_node() { + node.var_declared_names(vars); + } + } + Statement::DoWhileLoop(do_while_loop) => { + do_while_loop.body().var_declared_names(vars); + } + Statement::WhileLoop(while_loop) => { + while_loop.body().var_declared_names(vars); + } + Statement::ForLoop(for_loop) => { + if let Some(ForLoopInitializer::DeclarationList(DeclarationList::Var( + declarations, + ))) = for_loop.init() + { + for declaration in declarations.iter() { + match declaration.binding() { + Binding::Identifier(ident) => { + vars.insert(ident.sym()); + } + Binding::Pattern(pattern) => { + for ident in pattern.idents() { + vars.insert(ident); + } + } + } + } + } + for_loop.body().var_declared_names(vars); + } + Statement::ForInLoop(for_in_loop) => { + if let IterableLoopInitializer::Var(bind) = for_in_loop.init() { + vars.extend(bind.idents()); + } + for_in_loop.body().var_declared_names(vars); + } + Statement::ForOfLoop(for_of_loop) => { + if let IterableLoopInitializer::Var(bind) = for_of_loop.init() { + vars.extend(bind.idents()); + } + for_of_loop.body().var_declared_names(vars); + } + Statement::Switch(switch) => { + for case in switch.cases() { + for node in case.body().statements() { + node.var_declared_names(vars); + } + } + if let Some(stmts) = switch.default() { + stmts.var_declared_names(vars); + } + } + Statement::Try(try_statement) => { + for node in try_statement.block().statements() { + node.var_declared_names(vars); + } + if let Some(catch) = try_statement.catch() { + for node in catch.block().statements() { + node.var_declared_names(vars); + } + } + if let Some(finally) = try_statement.finally() { + for node in finally.statements() { + node.var_declared_names(vars); + } + } + } + _ => {} + } + } + + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + // TODO: replace with a visitor + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Statement::Expression(expr) => expr.contains_arguments(), + + Statement::Block(block) => block.statements().iter().any(Statement::contains_arguments), + Statement::DoWhileLoop(do_while_loop) => { + do_while_loop.body().contains_arguments() + || do_while_loop.cond().contains_arguments() + } + Statement::ForLoop(for_loop) => { + matches!(for_loop.init(), Some(expr) if expr.contains_arguments()) + || matches!(for_loop.condition(), Some(expr) if expr.contains_arguments()) + || matches!(for_loop.final_expr(), Some(expr) if expr.contains_arguments()) + || for_loop.body().contains_arguments() + } + Statement::ForInLoop(for_in_loop) => { + for_in_loop.init().contains_arguments() + || for_in_loop.expr().contains_arguments() + || for_in_loop.body().contains_arguments() + } + Statement::ForOfLoop(for_of_loop) => { + for_of_loop.init().contains_arguments() + || for_of_loop.iterable().contains_arguments() + || for_of_loop.body().contains_arguments() + } + Statement::If(r#if) => { + r#if.cond().contains_arguments() + || r#if.body().contains_arguments() + || matches!(r#if.else_node(), Some(node) if node.contains_arguments()) + } + + Statement::DeclarationList(decl_list) => decl_list + .as_ref() + .iter() + .any(Declaration::contains_arguments), + Statement::Return(r#return) => { + matches!(r#return.expr(), Some(expr) if expr.contains_arguments()) + } + Statement::Switch(switch) => { + switch.val().contains_arguments() + || switch.cases().iter().any(Case::contains_arguments) + } + Statement::Throw(throw) => throw.expr().contains_arguments(), + Statement::Try(r#try) => r#try.contains_arguments(), + Statement::WhileLoop(while_loop) => { + while_loop.condition().contains_arguments() + || while_loop.body().contains_arguments() + } + Statement::Class(class) => { + matches!(class.super_ref(), Some(expr) if expr.contains_arguments()) + || class + .elements() + .iter() + .any(ClassElement::contains_arguments) + } + _ => false, + } + } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + // TODO: replace with a visitor + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Statement::Expression(expr) => expr.contains(symbol), + + Statement::Block(block) => block.statements().iter().any(|stmt| stmt.contains(symbol)), + Statement::DoWhileLoop(do_while_loop) => { + do_while_loop.body().contains(symbol) || do_while_loop.cond().contains(symbol) + } + Statement::ForLoop(for_loop) => { + matches!(for_loop.init(), Some(expr) if expr.contains(symbol)) + || matches!(for_loop.condition(), Some(expr) if expr.contains(symbol)) + || matches!(for_loop.final_expr(), Some(expr) if expr.contains(symbol)) + || for_loop.body().contains(symbol) + } + Statement::ForInLoop(for_in_loop) => { + for_in_loop.init().contains(symbol) + || for_in_loop.expr().contains(symbol) + || for_in_loop.body().contains(symbol) + } + Statement::ForOfLoop(for_of_loop) => { + for_of_loop.init().contains(symbol) + || for_of_loop.iterable().contains(symbol) + || for_of_loop.body().contains(symbol) + } + Statement::If(r#if) => { + r#if.cond().contains(symbol) + || r#if.body().contains(symbol) + || matches!(r#if.else_node(), Some(expr) if expr.contains(symbol)) + } + + Statement::DeclarationList(decl_list) => { + decl_list.as_ref().iter().any(|decl| decl.contains(symbol)) + } + Statement::Return(r#return) => { + matches!(r#return.expr(), Some(expr) if expr.contains(symbol)) + } + Statement::Switch(switch) => { + switch.val().contains(symbol) + || switch.cases().iter().any(|case| case.contains(symbol)) + } + Statement::Throw(throw) => throw.expr().contains(symbol), + Statement::Try(r#try) => r#try.contains(symbol), + Statement::WhileLoop(while_loop) => { + while_loop.condition().contains(symbol) || while_loop.body().contains(symbol) + } + Statement::Class(class) => { + matches!(class.super_ref(), Some(expr) if expr.contains(symbol)) + || class.elements().iter().any(|elem| elem.contains(symbol)) + } + _ => false, + } + } +} + +impl ToInternedString for Statement { + fn to_interned_string(&self, interner: &Interner) -> String { + self.to_indented_string(interner, 0) + } +} diff --git a/boa_engine/src/syntax/ast/node/return_smt/mod.rs b/boa_engine/src/syntax/ast/statement/return.rs similarity index 64% rename from boa_engine/src/syntax/ast/node/return_smt/mod.rs rename to boa_engine/src/syntax/ast/statement/return.rs index e9b022b6904..8e08b51e524 100644 --- a/boa_engine/src/syntax/ast/node/return_smt/mod.rs +++ b/boa_engine/src/syntax/ast/statement/return.rs @@ -1,12 +1,6 @@ -use crate::syntax::ast::node::Node; +use crate::syntax::ast::{expression::Expression, statement::Statement}; use boa_interner::{Interner, Sym, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - /// The `return` statement ends function execution and specifies a value to be returned to the /// function caller. /// @@ -25,10 +19,10 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Return { - expr: Option>, + expr: Option, label: Option, } @@ -37,25 +31,17 @@ impl Return { self.label } - pub fn expr(&self) -> Option<&Node> { - self.expr.as_ref().map(Box::as_ref) + pub fn expr(&self) -> Option<&Expression> { + self.expr.as_ref() } /// Creates a `Return` AST node. - pub fn new(expr: OE, label: L) -> Self - where - E: Into, - OE: Into>, - L: Into>, - { - Self { - expr: expr.into().map(E::into).map(Box::new), - label: label.into(), - } + pub fn new(expr: Option, label: Option) -> Self { + Self { expr, label } } } -impl From for Node { +impl From for Statement { fn from(return_smt: Return) -> Self { Self::Return(return_smt) } @@ -69,3 +55,23 @@ impl ToInternedString 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/node/statement_list/mod.rs b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs similarity index 50% rename from boa_engine/src/syntax/ast/node/statement_list/mod.rs rename to boa_engine/src/syntax/ast/statement/statement_list/mod.rs index 719d8894bf0..6b77109adf5 100644 --- a/boa_engine/src/syntax/ast/node/statement_list/mod.rs +++ b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs @@ -1,11 +1,11 @@ //! Statement list node. -use crate::syntax::ast::node::{Declaration, Node}; +use crate::syntax::ast::statement::Statement; use boa_interner::{Interner, Sym, ToInternedString}; use rustc_hash::FxHashSet; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; + +use super::{declaration::Binding, DeclarationList}; #[cfg(test)] mod tests; @@ -18,18 +18,18 @@ mod tests; /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-StatementList -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct StatementList { - items: Box<[Node]>, + statements: Box<[Statement]>, strict: bool, } impl StatementList { - /// Gets the list of items. + /// Gets the list of statements. #[inline] - pub fn items(&self) -> &[Node] { - &self.items + pub fn statements(&self) -> &[Statement] { + &self.statements } /// Get the strict mode. @@ -45,20 +45,23 @@ impl StatementList { } /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = String::new(); // Print statements - for node in self.items.iter() { + for stmt in self.statements.iter() { // We rely on the node to add the correct indent. - buf.push_str(&node.to_indented_string(interner, indentation)); - - match node { - Node::Block(_) | Node::If(_) | Node::Switch(_) | Node::WhileLoop(_) => {} - _ => buf.push(';'), + buf.push_str(&stmt.to_indented_string(interner, indentation)); + + match stmt { + Statement::DeclarationList(_) + | Statement::Empty + | Statement::Expression(_) + | Statement::Continue(_) + | Statement::Break(_) + | Statement::Return(_) + | Statement::Throw(_) + | Statement::DoWhileLoop(_) => buf.push(';'), + _ => {} } buf.push('\n'); @@ -66,6 +69,12 @@ impl StatementList { buf } + pub(crate) 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`. /// /// The returned list may contain duplicates. @@ -79,32 +88,42 @@ impl StatementList { pub(crate) fn lexically_declared_names(&self) -> Vec<(Sym, bool)> { let mut names = Vec::new(); - for node in self.items() { + for node in self.statements() { match node { - Node::FunctionDecl(decl) => { - names.push((decl.name(), true)); + Statement::Function(decl) => { + if let Some(name) = decl.name() { + names.push((name, true)); + } } - Node::GeneratorDecl(decl) => { - names.push((decl.name(), false)); + Statement::Generator(decl) => { + if let Some(name) = decl.name() { + names.push((name, false)); + } } - Node::AsyncFunctionDecl(decl) => { - names.push((decl.name(), false)); + Statement::AsyncFunction(decl) => { + if let Some(name) = decl.name() { + names.push((name, false)); + } } - Node::AsyncGeneratorDecl(decl) => { - names.push((decl.name(), false)); + Statement::AsyncGenerator(decl) => { + if let Some(name) = decl.name() { + names.push((name, false)); + } } - Node::ClassDecl(decl) => { - names.push((decl.name(), false)); + Statement::Class(decl) => { + if let Some(name) = decl.name() { + names.push((name, false)); + } } - Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) => match decl_list { + Statement::DeclarationList(list) => match list { super::DeclarationList::Const(declarations) | super::DeclarationList::Let(declarations) => { for decl in declarations.iter() { - match decl { - Declaration::Identifier { ident, .. } => { + match decl.binding() { + Binding::Identifier(ident) => { names.push((ident.sym(), false)); } - Declaration::Pattern(pattern) => { + Binding::Pattern(pattern) => { names.extend( pattern.idents().into_iter().map(|name| (name, false)), ); @@ -112,8 +131,9 @@ impl StatementList { } } } - super::DeclarationList::Var(_) => unreachable!(), + super::DeclarationList::Var(_) => {} }, + _ => {} } } @@ -132,54 +152,42 @@ impl StatementList { pub(crate) fn lexically_declared_names_top_level(&self) -> Vec { let mut names = Vec::new(); - for node in self.items() { + for node in self.statements() { match node { - Node::ClassDecl(decl) => { - names.push(decl.name()); + Statement::Class(decl) => { + if let Some(name) = decl.name() { + names.push(name); + } } - Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) => match decl_list { - super::DeclarationList::Const(declarations) - | super::DeclarationList::Let(declarations) => { - for decl in declarations.iter() { - match decl { - Declaration::Identifier { ident, .. } => { - names.push(ident.sym()); - } - Declaration::Pattern(pattern) => { - names.extend(pattern.idents()); - } + Statement::DeclarationList( + DeclarationList::Const(declarations) | DeclarationList::Let(declarations), + ) => { + for decl in &**declarations { + match decl.binding() { + Binding::Identifier(ident) => { + names.push(ident.sym()); + } + Binding::Pattern(pattern) => { + names.extend(pattern.idents()); } } } - super::DeclarationList::Var(_) => unreachable!(), - }, + } _ => {} } } names } - - /// Return the variable declared names of a `StatementList`. - /// - /// More information: - /// - [ECMAScript specification][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames - pub(crate) fn var_declared_names_new(&self, vars: &mut FxHashSet) { - for node in self.items() { - node.var_declared_names(vars); - } - } } impl From for StatementList where - T: Into>, + T: Into>, { fn from(stm: T) -> Self { Self { - items: stm.into(), + statements: stm.into(), strict: false, } } diff --git a/boa_engine/src/syntax/ast/node/statement_list/tests.rs b/boa_engine/src/syntax/ast/statement/statement_list/tests.rs similarity index 100% rename from boa_engine/src/syntax/ast/node/statement_list/tests.rs rename to boa_engine/src/syntax/ast/statement/statement_list/tests.rs diff --git a/boa_engine/src/syntax/ast/node/switch/mod.rs b/boa_engine/src/syntax/ast/statement/switch/mod.rs similarity index 66% rename from boa_engine/src/syntax/ast/node/switch/mod.rs rename to boa_engine/src/syntax/ast/statement/switch/mod.rs index 9102a653f2e..cd1edfedce2 100644 --- a/boa_engine/src/syntax/ast/node/switch/mod.rs +++ b/boa_engine/src/syntax/ast/statement/switch/mod.rs @@ -1,38 +1,30 @@ //! Switch node. //! -use crate::syntax::ast::node::Node; +use crate::syntax::ast::{expression::Expression, statement::Statement}; use boa_interner::{Interner, ToInternedString}; -use crate::syntax::ast::node::StatementList; +use crate::syntax::ast::statement::StatementList; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::ContainsSymbol; #[cfg(test)] mod tests; -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Case { - condition: Node, + condition: Expression, body: StatementList, } impl Case { /// Creates a `Case` AST node. - pub fn new(condition: C, body: B) -> Self - where - C: Into, - B: Into, - { - Self { - condition: condition.into(), - body: body.into(), - } + pub fn new(condition: Expression, body: StatementList) -> Self { + Self { condition, body } } /// Gets the condition of the case. - pub fn condition(&self) -> &Node { + pub fn condition(&self) -> &Expression { &self.condition } @@ -40,6 +32,24 @@ impl Case { pub fn body(&self) -> &StatementList { &self.body } + + pub(crate) fn contains_arguments(&self) -> bool { + self.condition.contains_arguments() + || self + .body + .statements() + .iter() + .any(Statement::contains_arguments) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.condition.contains(symbol) + || self + .body + .statements() + .iter() + .any(|stmt| stmt.contains(symbol)) + } } /// The `switch` statement evaluates an expression, matching the expression's value to a case @@ -58,31 +68,26 @@ impl 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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Switch { - val: Box, + val: Expression, cases: Box<[Case]>, default: Option, } impl Switch { /// Creates a `Switch` AST node. - pub fn new(val: V, cases: C, default: Option) -> Self - where - V: Into, - C: Into>, - D: Into, - { + pub fn new(val: Expression, cases: Box<[Case]>, default: Option) -> Self { Self { - val: Box::new(val.into()), - cases: cases.into(), - default: default.map(D::into), + val, + cases, + default, } } /// Gets the value to switch. - pub fn val(&self) -> &Node { + pub fn val(&self) -> &Expression { &self.val } @@ -92,16 +97,12 @@ impl Switch { } /// Gets the default statement list, if any. - pub fn default(&self) -> Option<&[Node]> { - self.default.as_ref().map(StatementList::items) + pub fn default(&self) -> Option<&StatementList> { + self.default.as_ref() } /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let indent = " ".repeat(indentation); let mut buf = format!("switch ({}) {{\n", self.val().to_interned_string(interner)); for e in self.cases().iter() { @@ -131,7 +132,7 @@ impl ToInternedString for Switch { } } -impl From for Node { +impl From for Statement { fn from(switch: Switch) -> Self { Self::Switch(switch) } diff --git a/boa_engine/src/syntax/ast/node/switch/tests.rs b/boa_engine/src/syntax/ast/statement/switch/tests.rs similarity index 99% rename from boa_engine/src/syntax/ast/node/switch/tests.rs rename to boa_engine/src/syntax/ast/statement/switch/tests.rs index f5eec01949b..b9182b831d7 100644 --- a/boa_engine/src/syntax/ast/node/switch/tests.rs +++ b/boa_engine/src/syntax/ast/statement/switch/tests.rs @@ -208,7 +208,7 @@ fn bigger_switch_example() { #[test] fn fmt() { - super::super::test_formatting( + crate::syntax::ast::test_formatting( r#" let a = 3; let b = "unknown"; diff --git a/boa_engine/src/syntax/ast/node/throw/mod.rs b/boa_engine/src/syntax/ast/statement/throw.rs similarity index 60% rename from boa_engine/src/syntax/ast/node/throw/mod.rs rename to boa_engine/src/syntax/ast/statement/throw.rs index 5250e805a8b..da1b156ecd5 100644 --- a/boa_engine/src/syntax/ast/node/throw/mod.rs +++ b/boa_engine/src/syntax/ast/statement/throw.rs @@ -1,12 +1,6 @@ -use crate::syntax::ast::node::Node; +use crate::syntax::ast::{statement::Statement, Expression}; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -#[cfg(test)] -mod tests; - /// The `throw` statement throws a user-defined exception. /// /// Syntax: `throw expression;` @@ -21,36 +15,47 @@ mod tests; /// /// [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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Throw { - expr: Box, + expression: Expression, } impl Throw { - pub fn expr(&self) -> &Node { - &self.expr + pub fn expr(&self) -> &Expression { + &self.expression } /// Creates a `Throw` AST node. - pub fn new(val: V) -> Self - where - V: Into, - { - Self { - expr: Box::new(val.into()), - } + pub fn new(expression: Expression) -> Self { + Self { expression } } } impl ToInternedString for Throw { fn to_interned_string(&self, interner: &Interner) -> String { - format!("throw {}", self.expr.to_interned_string(interner)) + format!("throw {}", self.expression.to_interned_string(interner)) } } -impl From for Node { +impl From for Statement { fn from(trw: Throw) -> Self { Self::Throw(trw) } } + +#[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/node/try_node/mod.rs b/boa_engine/src/syntax/ast/statement/try/mod.rs similarity index 62% rename from boa_engine/src/syntax/ast/node/try_node/mod.rs rename to boa_engine/src/syntax/ast/statement/try/mod.rs index 526f9c2b2bb..a58475e1397 100644 --- a/boa_engine/src/syntax/ast/node/try_node/mod.rs +++ b/boa_engine/src/syntax/ast/statement/try/mod.rs @@ -1,8 +1,7 @@ -use crate::syntax::ast::node::{Block, Declaration, Node}; +use crate::syntax::ast::statement::{Block, Statement}; use boa_interner::{Interner, ToInternedString}; -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; +use super::{declaration::Binding, ContainsSymbol}; #[cfg(test)] mod tests; @@ -20,7 +19,7 @@ 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(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Try { block: Block, @@ -30,21 +29,18 @@ pub struct Try { impl Try { /// Creates a new `Try` AST node. - pub(in crate::syntax) fn new( - block: B, + pub(in crate::syntax) fn new( + block: Block, catch: Option, finally: Option, - ) -> Self - where - B: Into, - { + ) -> Self { assert!( catch.is_some() || finally.is_some(), "one of catch or finally must be pressent" ); Self { - block: block.into(), + block, catch, finally, } @@ -65,12 +61,26 @@ impl Try { self.finally.as_ref().map(Finally::block) } + pub(crate) fn contains_arguments(&self) -> bool { + self.block + .statements() + .iter() + .any(Statement::contains_arguments) + || matches!(self.catch, Some(ref catch) if catch.contains_arguments()) + || matches!(self.finally, Some(ref finally) if finally.contains_arguments()) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.block + .statements() + .iter() + .any(|stmt| stmt.contains(symbol)) + || matches!(self.catch, Some(ref catch) if catch.contains(symbol)) + || matches!(self.finally, Some(ref finally) if finally.contains(symbol)) + } + /// Implements the display formatting with indentation. - pub(in crate::syntax::ast::node) fn to_indented_string( - &self, - interner: &Interner, - indentation: usize, - ) -> String { + pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = format!( "{}try {}", " ".repeat(indentation), @@ -94,37 +104,29 @@ impl ToInternedString for Try { } } -impl From for Node { +impl From for Statement { fn from(try_catch: Try) -> Self { - Self::Try(Box::new(try_catch)) + Self::Try(try_catch) } } /// Catch block. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Catch { - parameter: Option>, + parameter: Option, block: Block, } impl Catch { /// Creates a new catch block. - pub(in crate::syntax) fn new(parameter: OD, block: B) -> Self - where - OD: Into>, - D: Into, - B: Into, - { - Self { - parameter: parameter.into().map(|d| Box::new(d.into())), - block: block.into(), - } + pub(in crate::syntax) fn new(parameter: Option, block: Block) -> Self { + Self { parameter, block } } /// Gets the parameter of the catch block. - pub fn parameter(&self) -> Option<&Declaration> { - self.parameter.as_deref() + pub fn parameter(&self) -> Option<&Binding> { + self.parameter.as_ref() } /// Retrieves the catch execution block. @@ -132,6 +134,20 @@ impl Catch { &self.block } + pub(crate) fn contains_arguments(&self) -> bool { + self.block + .statements() + .iter() + .any(Statement::contains_arguments) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.block + .statements() + .iter() + .any(|stmt| stmt.contains(symbol)) + } + /// Implements the display formatting with indentation. pub(super) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = " catch".to_owned(); @@ -154,7 +170,7 @@ impl ToInternedString for Catch { } /// Finally block. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Finally { block: Block, @@ -166,6 +182,20 @@ impl Finally { &self.block } + pub(crate) fn contains_arguments(&self) -> bool { + self.block + .statements() + .iter() + .any(Statement::contains_arguments) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.block + .statements() + .iter() + .any(|stmt| stmt.contains(symbol)) + } + /// Implements the display formatting with indentation. pub(super) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { format!( diff --git a/boa_engine/src/syntax/ast/node/try_node/tests.rs b/boa_engine/src/syntax/ast/statement/try/tests.rs similarity index 97% rename from boa_engine/src/syntax/ast/node/try_node/tests.rs rename to boa_engine/src/syntax/ast/statement/try/tests.rs index bb50c535b3f..4cd55ebe8f8 100644 --- a/boa_engine/src/syntax/ast/node/try_node/tests.rs +++ b/boa_engine/src/syntax/ast/statement/try/tests.rs @@ -128,7 +128,7 @@ fn catch_binding_finally() { #[test] fn fmt() { - super::super::test_formatting( + crate::syntax::ast::test_formatting( r#" try { throw "hello"; @@ -136,12 +136,12 @@ fn fmt() { console.log(e); } finally { console.log("things"); - }; + } try { throw "hello"; } catch { console.log("something went wrong"); - }; + } "#, ); } diff --git a/boa_engine/src/syntax/parser/error.rs b/boa_engine/src/syntax/parser/error.rs index 80f69d5be98..1cf7d5f49df 100644 --- a/boa_engine/src/syntax/parser/error.rs +++ b/boa_engine/src/syntax/parser/error.rs @@ -1,12 +1,12 @@ //! Error and result implementation for the parser. +use crate::syntax::ast::position::Position; use crate::syntax::ast::Span; -use crate::syntax::ast::{position::Position, Node}; use crate::syntax::lexer::Error as LexError; use std::fmt; /// Result of a parsing operation. -pub type ParseResult = Result; +pub type ParseResult = Result; pub(crate) trait ErrorContext { fn context(self, context: &'static str) -> Self; 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 112ed1d22f9..8b3d025a98c 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs @@ -10,11 +10,10 @@ use super::AssignmentExpression; use crate::syntax::{ ast::{ - node::{ - declaration::Declaration, ArrowFunctionDecl, FormalParameter, FormalParameterList, - FormalParameterListFlags, Node, Return, StatementList, - }, - Punctuator, + self, + function::{FormalParameter, FormalParameterList, FormalParameterListFlags}, + statement::{declaration::Declaration, Return, StatementList}, + Expression, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ @@ -71,13 +70,9 @@ impl TokenParser for ArrowFunction where R: Read, { - type Output = ArrowFunctionDecl; + type Output = ast::function::ArrowFunction; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ArrowFunction", "Parsing"); let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -106,7 +101,7 @@ where ( FormalParameterList::new( Box::new([FormalParameter::new( - Declaration::new_with_identifier(param, None), + Declaration::from_identifier(param.into(), None), false, )]), flags, @@ -152,7 +147,7 @@ where params_start_position, )?; - Ok(ArrowFunctionDecl::new(self.name, params, body)) + Ok(ast::function::ArrowFunction::new(self.name, params, body)) } } @@ -180,11 +175,7 @@ where { type Output = StatementList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { match cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? @@ -197,7 +188,9 @@ where Ok(body) } _ => Ok(StatementList::from(vec![Return::new( - ExpressionBody::new(self.allow_in, false).parse(cursor, interner)?, + ExpressionBody::new(self.allow_in, false) + .parse(cursor, interner)? + .into(), None, ) .into()])), @@ -230,9 +223,9 @@ impl TokenParser for ExpressionBody where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { AssignmentExpression::new(None, self.allow_in, false, self.allow_await) .parse(cursor, interner) } diff --git a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs index 67b9b8756ce..662d43ab49f 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs @@ -8,7 +8,7 @@ //! [spec]: https://tc39.es/ecma262/#sec-conditional-operator use crate::syntax::{ - ast::{node::ConditionalOp, Node, Punctuator}, + ast::{expression::operator::conditional::Conditional, Expression, Punctuator}, lexer::TokenKind, parser::{ expression::{AssignmentExpression, ShortCircuitExpression}, @@ -62,9 +62,9 @@ impl TokenParser for ConditionalExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ConditionalExpression", "Parsing"); let lhs = ShortCircuitExpression::new( self.name, @@ -93,7 +93,7 @@ where self.allow_await, ) .parse(cursor, interner)?; - return Ok(ConditionalOp::new(lhs, then_clause, else_clause).into()); + return Ok(Conditional::new(lhs, then_clause, else_clause).into()); } } diff --git a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs index 61197f48080..69073a6b798 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs @@ -10,9 +10,8 @@ use super::ParseError; use crate::syntax::{ ast::{ - node::{BinOp, Node}, - op::NumOp, - Keyword, Punctuator, + expression::operator::{binary::op::ArithmeticOp, Binary}, + Expression, Keyword, Punctuator, }, lexer::TokenKind, parser::{ @@ -63,9 +62,9 @@ impl TokenParser for ExponentiationExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ExponentiationExpression", "Parsing"); let next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -89,7 +88,12 @@ where if let Some(tok) = cursor.peek(0, interner)? { if let TokenKind::Punctuator(Punctuator::Exp) = tok.kind() { cursor.next(interner)?.expect("** token vanished"); // Consume the token. - return Ok(BinOp::new(NumOp::Exp, lhs, self.parse(cursor, interner)?).into()); + return Ok(Binary::new( + ArithmeticOp::Exp.into(), + lhs, + self.parse(cursor, interner)?, + ) + .into()); } } Ok(lhs) diff --git a/boa_engine/src/syntax/parser/expression/assignment/mod.rs b/boa_engine/src/syntax/parser/expression/assignment/mod.rs index cfc892e5367..0065b64ab16 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/mod.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/mod.rs @@ -14,8 +14,9 @@ mod r#yield; use crate::syntax::{ ast::{ - node::{operator::assign::AssignTarget, ArrowFunctionDecl, Assign, BinOp, Node}, - Keyword, Punctuator, + self, + expression::operator::assign::{op::AssignOp, Assign, AssignTarget}, + Expression, Keyword, Punctuator, }, lexer::{Error as LexError, InputElement, TokenKind}, parser::{ @@ -86,9 +87,9 @@ impl TokenParser for AssignmentExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("AssignmentExpression", "Parsing"); cursor.set_goal(InputElement::RegExp); @@ -127,7 +128,7 @@ where self.allow_await, ) .parse(cursor, interner) - .map(Node::ArrowFunctionDecl); + .map(Expression::ArrowFunction); } } } @@ -150,7 +151,7 @@ where .parse(cursor, interner)?; // If the left hand side is a parameter list, we must parse an arrow function. - if let Node::FormalParameterList(parameters) = lhs { + if let Expression::FormalParameterList(parameters) = lhs { cursor.peek_expect_no_lineterminator(0, "arrow function", interner)?; cursor.expect( @@ -189,16 +190,15 @@ where position, )?; - return Ok(ArrowFunctionDecl::new(self.name, parameters, body).into()); + return Ok(ast::function::ArrowFunction::new(self.name, parameters, body).into()); } // Review if we are trying to assign to an invalid left hand side expression. - // TODO: can we avoid cloning? if let Some(tok) = cursor.peek(0, interner)?.cloned() { match tok.kind() { TokenKind::Punctuator(Punctuator::Assign) => { if cursor.strict_mode() { - if let Node::Identifier(ident) = lhs { + if let Expression::Identifier(ident) = lhs { ident.check_strict_arguments_or_eval(position)?; } } @@ -206,12 +206,14 @@ where cursor.next(interner)?.expect("= token vanished"); cursor.set_goal(InputElement::RegExp); - if let Some(target) = AssignTarget::from_node(&lhs, cursor.strict_mode()) { + if let Some(target) = + AssignTarget::from_expression(&lhs, cursor.strict_mode(), true) + { if let AssignTarget::Identifier(ident) = target { self.name = Some(ident.sym()); } let expr = self.parse(cursor, interner)?; - lhs = Assign::new(target, expr).into(); + lhs = Assign::new(AssignOp::Assign, target, expr).into(); } else { return Err(ParseError::lex(LexError::Syntax( "Invalid left-hand side in assignment".into(), @@ -219,18 +221,20 @@ where ))); } } - TokenKind::Punctuator(p) if p.as_binop().is_some() && p != &Punctuator::Comma => { + TokenKind::Punctuator(p) if p.as_assign_op().is_some() => { if cursor.strict_mode() { - if let Node::Identifier(ident) = lhs { + if let Expression::Identifier(ident) = lhs { ident.check_strict_arguments_or_eval(position)?; } } cursor.next(interner)?.expect("token vanished"); - if is_assignable(&lhs) { - let binop = p.as_binop().expect("binop disappeared"); - let expr = self.parse(cursor, interner)?; - lhs = BinOp::new(binop, lhs, expr).into(); + if let Some(target) = + AssignTarget::from_expression(&lhs, cursor.strict_mode(), false) + { + let assignop = p.as_assign_op().expect("assignop disappeared"); + let rhs = self.parse(cursor, interner)?; + lhs = Assign::new(assignop, target, rhs).into(); } else { return Err(ParseError::lex(LexError::Syntax( "Invalid left-hand side in assignment".into(), @@ -245,19 +249,3 @@ where Ok(lhs) } } - -/// Returns true if as per spec[spec] the node can be assigned a value. -/// -/// [spec]: https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors -#[inline] -pub(crate) fn is_assignable(node: &Node) -> bool { - matches!( - node, - Node::GetConstField(_) - | Node::GetField(_) - | Node::Assign(_) - | Node::Call(_) - | Node::Identifier(_) - | Node::Object(_) - ) -} diff --git a/boa_engine/src/syntax/parser/expression/assignment/yield.rs b/boa_engine/src/syntax/parser/expression/assignment/yield.rs index 3632ad667ac..0cb033756c6 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/yield.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/yield.rs @@ -9,10 +9,7 @@ use super::AssignmentExpression; use crate::syntax::{ - ast::{ - node::{Node, Yield}, - Keyword, Punctuator, - }, + ast::{expression::Yield, Expression, Keyword, Punctuator}, lexer::TokenKind, parser::{AllowAwait, AllowIn, Cursor, ParseError, ParseResult, TokenParser}, }; @@ -52,9 +49,9 @@ impl TokenParser for YieldExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("YieldExpression", "Parsing"); cursor.expect( @@ -67,7 +64,7 @@ where cursor.peek_is_line_terminator(0, interner)?, Some(true) | None ) { - return Ok(Node::Yield(Yield::new::(None, false))); + return Ok(Yield::new(None, false).into()); } let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -76,7 +73,7 @@ where cursor.next(interner)?.expect("token disappeared"); let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await) .parse(cursor, interner)?; - Ok(Node::Yield(Yield::new(Some(expr), true))) + Ok(Yield::new(Some(expr), true).into()) } TokenKind::Identifier(_) | TokenKind::Punctuator( @@ -113,9 +110,9 @@ where | TokenKind::TemplateMiddle(_) => { let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await) .parse(cursor, interner)?; - Ok(Node::Yield(Yield::new(Some(expr), false))) + Ok(Yield::new(Some(expr), false).into()) } - _ => Ok(Node::Yield(Yield::new::(None, false))), + _ => Ok(Yield::new(None, false).into()), } } } diff --git a/boa_engine/src/syntax/parser/expression/await_expr.rs b/boa_engine/src/syntax/parser/expression/await_expr.rs index 3f11ca7a230..2042685b31c 100644 --- a/boa_engine/src/syntax/parser/expression/await_expr.rs +++ b/boa_engine/src/syntax/parser/expression/await_expr.rs @@ -9,9 +9,9 @@ use super::unary::UnaryExpression; use crate::syntax::{ - ast::{node::AwaitExpr, Keyword}, + ast::{expression::Await, Keyword}, lexer::TokenKind, - parser::{AllowYield, Cursor, ParseError, TokenParser}, + parser::{AllowYield, Cursor, ParseResult, TokenParser}, }; use boa_interner::Interner; use std::io::Read; @@ -45,13 +45,9 @@ impl TokenParser for AwaitExpression where R: Read, { - type Output = AwaitExpr; + type Output = Await; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect( TokenKind::Keyword((Keyword::Await, false)), "Await expression parsing", diff --git a/boa_engine/src/syntax/parser/expression/identifiers.rs b/boa_engine/src/syntax/parser/expression/identifiers.rs index d4981d89c1d..9f6ac3893a4 100644 --- a/boa_engine/src/syntax/parser/expression/identifiers.rs +++ b/boa_engine/src/syntax/parser/expression/identifiers.rs @@ -7,9 +7,9 @@ //! use crate::syntax::{ - ast::{node::Identifier, Keyword}, + ast::{expression::Identifier, Keyword}, lexer::{Error as LexError, TokenKind}, - parser::{cursor::Cursor, AllowAwait, AllowYield, ParseError, TokenParser}, + parser::{cursor::Cursor, AllowAwait, AllowYield, ParseError, ParseResult, TokenParser}, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -59,11 +59,7 @@ where { type Output = Identifier; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("IdentifierReference", "Parsing"); let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; @@ -155,11 +151,7 @@ where type Output = Sym; /// Strict mode parsing as per . - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("BindingIdentifier", "Parsing"); let next_token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; 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 fb68e5e6cec..9cd66f8d51b 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,10 +8,11 @@ //! [spec]: https://tc39.es/ecma262/#prod-Arguments use crate::syntax::{ - ast::{node::Spread, Node, Punctuator}, + ast::{expression::Spread, Expression, Punctuator}, lexer::{InputElement, TokenKind}, parser::{ - expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser, + expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, + TokenParser, }, }; use boa_interner::Interner; @@ -50,13 +51,9 @@ impl TokenParser for Arguments where R: Read, { - type Output = Box<[Node]>; + type Output = Box<[Expression]>; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Arguments", "Parsing"); cursor.expect(Punctuator::OpenParen, "arguments", interner)?; 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 2f9e0ea6ba4..c56840c8850 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 @@ -10,9 +10,10 @@ use super::arguments::Arguments; use crate::syntax::{ ast::{ - node::{ - field::{get_private_field::GetPrivateField, GetConstField, GetField}, - Call, Node, + self, + expression::{ + access::{PrivatePropertyAccess, PropertyAccess}, + Call, }, Punctuator, }, @@ -36,12 +37,16 @@ use std::io::Read; pub(super) struct CallExpression { allow_yield: AllowYield, allow_await: AllowAwait, - first_member_expr: Node, + first_member_expr: ast::Expression, } impl CallExpression { /// Creates a new `CallExpression` parser. - pub(super) fn new(allow_yield: Y, allow_await: A, first_member_expr: Node) -> Self + pub(super) fn new( + allow_yield: Y, + allow_await: A, + first_member_expr: ast::Expression, + ) -> Self where Y: Into, A: Into, @@ -58,9 +63,9 @@ impl TokenParser for CallExpression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("CallExpression", "Parsing"); let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -68,7 +73,7 @@ where let mut lhs = if token.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) { let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; - Node::from(Call::new(self.first_member_expr, args)) + Call::new(self.first_member_expr, args).into() } else { let next_token = cursor.next(interner)?.expect("token vanished"); return Err(ParseError::expected( @@ -85,30 +90,30 @@ where TokenKind::Punctuator(Punctuator::OpenParen) => { let args = Arguments::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - lhs = Node::from(Call::new(lhs, args)); + lhs = ast::Expression::from(Call::new(lhs, args)); } TokenKind::Punctuator(Punctuator::Dot) => { cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; // We move the parser forward. match cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?.kind() { TokenKind::Identifier(name) => { - lhs = GetConstField::new(lhs, *name).into(); + lhs = PropertyAccess::new(lhs, *name).into(); } TokenKind::Keyword((kw, _)) => { - lhs = GetConstField::new(lhs, kw.to_sym(interner)).into(); + lhs = PropertyAccess::new(lhs, kw.to_sym(interner)).into(); } TokenKind::BooleanLiteral(true) => { - lhs = GetConstField::new(lhs, Sym::TRUE).into(); + lhs = PropertyAccess::new(lhs, Sym::TRUE).into(); } TokenKind::BooleanLiteral(false) => { - lhs = GetConstField::new(lhs, Sym::FALSE).into(); + lhs = PropertyAccess::new(lhs, Sym::FALSE).into(); } TokenKind::NullLiteral => { - lhs = GetConstField::new(lhs, Sym::NULL).into(); + lhs = PropertyAccess::new(lhs, Sym::NULL).into(); } TokenKind::PrivateIdentifier(name) => { cursor.push_used_private_identifier(*name, token.span().start())?; - lhs = GetPrivateField::new(lhs, *name).into(); + lhs = PrivatePropertyAccess::new(lhs, *name).into(); } _ => { return Err(ParseError::expected( @@ -125,7 +130,7 @@ where let idx = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "call expression", interner)?; - lhs = GetField::new(lhs, idx).into(); + lhs = PropertyAccess::new(lhs, idx).into(); } TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => { lhs = TaggedTemplateLiteral::new( @@ -134,7 +139,8 @@ where tok.span().start(), lhs, ) - .parse(cursor, interner)?; + .parse(cursor, interner)? + .into(); } _ => break, } 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 a62981512f2..b1807d6cde9 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 @@ -8,9 +8,12 @@ use super::arguments::Arguments; use crate::syntax::{ ast::{ - node::{ - field::{get_private_field::GetPrivateField, GetConstField, GetField}, - Call, GetSuperField, New, Node, + self, + expression::{ + access::{ + PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SuperPropertyAccess, + }, + Call, New, }, Keyword, Punctuator, }, @@ -59,9 +62,9 @@ impl TokenParser for MemberExpression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("MemberExpression", "Parsing"); let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -78,7 +81,9 @@ where if cursor.next_if(Punctuator::Dot, interner)?.is_some() { let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; match token.kind() { - TokenKind::Identifier(Sym::TARGET) => return Ok(Node::NewTarget), + TokenKind::Identifier(Sym::TARGET) => { + return Ok(ast::Expression::NewTarget) + } _ => { return Err(ParseError::general( "unexpected private identifier", @@ -98,7 +103,7 @@ where }; let call_node = Call::new(lhs, args); - Node::from(New::from(call_node)) + ast::Expression::from(New::from(call_node)) } TokenKind::Keyword((Keyword::Super, _)) => { cursor.next(interner).expect("token disappeared"); @@ -107,11 +112,19 @@ where TokenKind::Punctuator(Punctuator::Dot) => { let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; let field = match token.kind() { - TokenKind::Identifier(name) => GetSuperField::from(*name), - TokenKind::Keyword((kw, _)) => GetSuperField::from(kw.to_sym(interner)), - TokenKind::BooleanLiteral(true) => GetSuperField::from(Sym::TRUE), - TokenKind::BooleanLiteral(false) => GetSuperField::from(Sym::FALSE), - TokenKind::NullLiteral => GetSuperField::from(Sym::NULL), + TokenKind::Identifier(name) => { + SuperPropertyAccess::new(PropertyAccessField::from(*name)) + } + TokenKind::Keyword((kw, _)) => { + SuperPropertyAccess::new(kw.to_sym(interner).into()) + } + TokenKind::BooleanLiteral(true) => { + SuperPropertyAccess::new(Sym::TRUE.into()) + } + TokenKind::BooleanLiteral(false) => { + SuperPropertyAccess::new(Sym::FALSE.into()) + } + TokenKind::NullLiteral => SuperPropertyAccess::new(Sym::NULL.into()), TokenKind::PrivateIdentifier(_) => { return Err(ParseError::general( "unexpected private identifier", @@ -123,7 +136,7 @@ where token.to_string(interner), token.span(), "expected super property", - )) + )); } }; field.into() @@ -132,7 +145,7 @@ where let expr = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "super property", interner)?; - GetSuperField::from(expr).into() + ast::Expression::from(SuperPropertyAccess::new(expr.into())) } _ => { return Err(ParseError::unexpected( @@ -157,22 +170,22 @@ where let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; match token.kind() { - TokenKind::Identifier(name) => lhs = GetConstField::new(lhs, *name).into(), + TokenKind::Identifier(name) => lhs = PropertyAccess::new(lhs, *name).into(), TokenKind::Keyword((kw, _)) => { - lhs = GetConstField::new(lhs, kw.to_sym(interner)).into(); + lhs = PropertyAccess::new(lhs, kw.to_sym(interner)).into(); } TokenKind::BooleanLiteral(true) => { - lhs = GetConstField::new(lhs, Sym::TRUE).into(); + lhs = PropertyAccess::new(lhs, Sym::TRUE).into(); } TokenKind::BooleanLiteral(false) => { - lhs = GetConstField::new(lhs, Sym::FALSE).into(); + lhs = PropertyAccess::new(lhs, Sym::FALSE).into(); } TokenKind::NullLiteral => { - lhs = GetConstField::new(lhs, Sym::NULL).into(); + lhs = PropertyAccess::new(lhs, Sym::NULL).into(); } TokenKind::PrivateIdentifier(name) => { cursor.push_used_private_identifier(*name, token.span().start())?; - lhs = GetPrivateField::new(lhs, *name).into(); + lhs = PrivatePropertyAccess::new(lhs, *name).into(); } _ => { return Err(ParseError::expected( @@ -191,7 +204,7 @@ where let idx = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBracket, "member expression", interner)?; - lhs = GetField::new(lhs, idx).into(); + lhs = PropertyAccess::new(lhs, idx).into(); } TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => { lhs = TaggedTemplateLiteral::new( @@ -200,7 +213,8 @@ where tok.span().start(), lhs, ) - .parse(cursor, interner)?; + .parse(cursor, interner)? + .into(); } _ => break, } 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 9141cb04454..f0ee09c7d51 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 @@ -16,7 +16,7 @@ mod member; mod template; use crate::syntax::{ - ast::{node::SuperCall, Keyword, Node, Punctuator}, + ast::{expression::SuperCall, Expression, Keyword, Punctuator}, lexer::{InputElement, TokenKind}, parser::{ expression::left_hand_side::{ @@ -64,10 +64,10 @@ impl TokenParser for LeftHandSideExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { - let _timer = Profiler::global().start_event("LeftHandSIdeExpression", "Parsing"); + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + let _timer = Profiler::global().start_event("LeftHandSideExpression", "Parsing"); cursor.set_goal(InputElement::TemplateTail); 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 9e2a98577fe..d8b35183536 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,6 +1,5 @@ use crate::syntax::{ - ast::node::TaggedTemplate, - ast::{Node, Position, Punctuator}, + ast::{self, expression::TaggedTemplate, Position, Punctuator}, lexer::TokenKind, parser::{ cursor::Cursor, expression::Expression, AllowAwait, AllowYield, ParseError, ParseResult, @@ -22,12 +21,17 @@ pub(super) struct TaggedTemplateLiteral { allow_yield: AllowYield, allow_await: AllowAwait, start: Position, - tag: Node, + tag: ast::Expression, } impl TaggedTemplateLiteral { /// Creates a new `TaggedTemplateLiteral` parser. - pub(super) fn new(allow_yield: Y, allow_await: A, start: Position, tag: Node) -> Self + pub(super) fn new( + allow_yield: Y, + allow_await: A, + start: Position, + tag: ast::Expression, + ) -> Self where Y: Into, A: Into, @@ -45,9 +49,9 @@ impl TokenParser for TaggedTemplateLiteral where R: Read, { - type Output = Node; + type Output = TaggedTemplate; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("TaggedTemplateLiteral", "Parsing"); let mut raws = Vec::new(); @@ -74,9 +78,12 @@ where TokenKind::TemplateNoSubstitution(template_string) => { raws.push(template_string.as_raw()); cookeds.push(template_string.to_owned_cooked(interner).ok()); - return Ok(Node::from(TaggedTemplate::new( - self.tag, raws, cookeds, exprs, - ))); + return Ok(TaggedTemplate::new( + self.tag, + raws.into_boxed_slice(), + cookeds.into_boxed_slice(), + exprs.into_boxed_slice(), + )); } _ => { return Err(ParseError::general( 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 8e511879ab4..86e67870d68 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,24 +1,26 @@ -use crate::{ - string::utf16, - syntax::{ - ast::node::{field::GetConstField, Call, Identifier}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::{access::PropertyAccess, Call, Identifier}, + Expression, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; macro_rules! check_call_property_identifier { ($property:literal) => {{ let mut interner = Interner::default(); check_parser( format!("a().{}", $property).as_str(), - vec![GetConstField::new( + vec![Expression::from(PropertyAccess::new( Call::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - vec![], - ), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Box::default(), + ) + .into(), interner.get_or_intern_static($property, utf16!($property)), - ) + )) .into()], interner, ); @@ -39,10 +41,10 @@ macro_rules! check_member_property_identifier { let mut interner = Interner::default(); check_parser( format!("a.{}", $property).as_str(), - vec![GetConstField::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), + vec![Expression::from(PropertyAccess::new( + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), interner.get_or_intern_static($property, utf16!($property)), - ) + )) .into()], interner, ); diff --git a/boa_engine/src/syntax/parser/expression/mod.rs b/boa_engine/src/syntax/parser/expression/mod.rs index 406da35ec14..caa1eb691ff 100644 --- a/boa_engine/src/syntax/parser/expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/mod.rs @@ -20,9 +20,12 @@ pub(in crate::syntax::parser) mod await_expr; mod tests; use crate::syntax::{ - ast::op::LogOp, ast::{ - node::{BinOp, Node}, + self, + expression::operator::{ + binary::op::{BinaryOp, LogicalOp}, + Binary, + }, Keyword, Punctuator, }, lexer::{InputElement, TokenKind}, @@ -83,9 +86,9 @@ macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $lo where R: Read { - type Output = Node; + type Output = ast::Expression; - fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult { + fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult { let _timer = Profiler::global().start_event(stringify!($name), "Parsing"); if $goal.is_some() { @@ -98,16 +101,16 @@ macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $lo match *tok.kind() { TokenKind::Punctuator(op) if $( op == $op )||* => { let _next = cursor.next(interner).expect("token disappeared"); - lhs = BinOp::new( - op.as_binop().expect("Could not get binary operation."), + 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 = BinOp::new( - op.as_binop().expect("Could not get binary operation."), + lhs = Binary::new( + op.as_binary_op().expect("Could not get binary operation."), lhs, $lower::new($( self.$low_param ),*).parse(cursor, interner)? ).into(); @@ -159,9 +162,13 @@ impl TokenParser for Expression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse( + mut self, + cursor: &mut Cursor, + interner: &mut Interner, + ) -> ParseResult { let _timer = Profiler::global().start_event("Expression", "Parsing"); let mut lhs = @@ -191,9 +198,9 @@ where let _next = cursor.next(interner).expect("token disappeared"); - lhs = BinOp::new( + lhs = Binary::new( Punctuator::Comma - .as_binop() + .as_binary_op() .expect("Could not get binary operation."), lhs, AssignmentExpression::new( @@ -283,9 +290,9 @@ impl TokenParser for ShortCircuitExpression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ShortCircuitExpression", "Parsing"); let mut current_node = @@ -313,7 +320,8 @@ where ) .parse(cursor, interner)?; - current_node = BinOp::new(LogOp::And, current_node, rhs).into(); + current_node = + Binary::new(BinaryOp::Logical(LogicalOp::And), current_node, rhs).into(); } TokenKind::Punctuator(Punctuator::BoolOr) => { if previous == PreviousExpr::Coalesce { @@ -333,7 +341,8 @@ where PreviousExpr::Logical, ) .parse(cursor, interner)?; - current_node = BinOp::new(LogOp::Or, current_node, rhs).into(); + current_node = + Binary::new(BinaryOp::Logical(LogicalOp::Or), current_node, rhs).into(); } TokenKind::Punctuator(Punctuator::Coalesce) => { if previous == PreviousExpr::Logical { @@ -353,7 +362,9 @@ where self.allow_await, ) .parse(cursor, interner)?; - current_node = BinOp::new(LogOp::Coalesce, current_node, rhs).into(); + current_node = + Binary::new(BinaryOp::Logical(LogicalOp::Coalesce), current_node, rhs) + .into(); } _ => break, } @@ -573,9 +584,9 @@ impl TokenParser for RelationalExpression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Relation Expression", "Parsing"); let mut lhs = ShiftExpression::new(self.name, self.allow_yield, self.allow_await) @@ -589,8 +600,8 @@ where || op == Punctuator::GreaterThanOrEq => { let _next = cursor.next(interner).expect("token disappeared"); - lhs = BinOp::new( - op.as_binop().expect("Could not get binary operation."), + lhs = Binary::new( + op.as_binary_op().expect("Could not get binary operation."), lhs, ShiftExpression::new(self.name, self.allow_yield, self.allow_await) .parse(cursor, interner)?, @@ -608,8 +619,8 @@ where || (op == Keyword::In && self.allow_in == AllowIn(true)) => { let _next = cursor.next(interner).expect("token disappeared"); - lhs = BinOp::new( - op.as_binop().expect("Could not get binary operation."), + lhs = Binary::new( + op.as_binary_op().expect("Could not get binary operation."), lhs, ShiftExpression::new(self.name, self.allow_yield, self.allow_await) .parse(cursor, interner)?, 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 df9097f3e01..5d1050b4d21 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 @@ -12,12 +12,13 @@ mod tests; use crate::syntax::{ ast::{ - node::{ArrayDecl, Node, Spread}, + expression::{literal, Spread}, Punctuator, }, lexer::TokenKind, parser::{ - expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser, + expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, + TokenParser, }, }; use boa_interner::Interner; @@ -56,13 +57,9 @@ impl TokenParser for ArrayLiteral where R: Read, { - type Output = ArrayDecl; + type Output = literal::ArrayLiteral; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ArrayLiteral", "Parsing"); let mut elements = Vec::new(); let mut has_trailing_comma_spread = false; @@ -90,7 +87,7 @@ where } TokenKind::Punctuator(Punctuator::Comma) => { cursor.next(interner).expect("token disappeared"); - elements.push(Node::Empty); + elements.push(None); } TokenKind::Punctuator(Punctuator::Spread) if next_comma => { return Err(ParseError::unexpected( @@ -104,7 +101,7 @@ where let node = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - elements.push(Spread::new(node).into()); + elements.push(Some(Spread::new(node).into())); next_comma = true; last_spread = true; } @@ -116,10 +113,10 @@ where )); } _ => { - let node = + let expr = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - elements.push(node); + elements.push(Some(expr)); next_comma = true; last_spread = false; } @@ -127,11 +124,14 @@ where } if last_spread { - if let Some(Node::Empty) = elements.last() { + if let Some(None) = elements.last() { has_trailing_comma_spread = true; } } - Ok(ArrayDecl::new(elements, has_trailing_comma_spread)) + Ok(literal::ArrayLiteral::new( + elements, + has_trailing_comma_spread, + )) } } 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 063dca51550..a25a4e82db7 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,20 +1,21 @@ // ! Tests for array initializer parsing. -use crate::{ - string::utf16, - syntax::{ - ast::{node::ArrayDecl, Const, Node}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::literal::{ArrayLiteral, Literal}, + Expression, }, + parser::tests::check_parser, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; /// Checks an empty array. #[test] fn check_empty() { check_parser( "[]", - vec![ArrayDecl::from(vec![]).into()], + vec![Expression::from(ArrayLiteral::from(vec![])).into()], Interner::default(), ); } @@ -24,7 +25,7 @@ fn check_empty() { fn check_empty_slot() { check_parser( "[,]", - vec![ArrayDecl::from(vec![Node::Empty]).into()], + vec![Expression::from(ArrayLiteral::from(vec![None])).into()], Interner::default(), ); } @@ -34,11 +35,11 @@ fn check_empty_slot() { fn check_numeric_array() { check_parser( "[1, 2, 3]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(2).into(), - Const::from(3).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(2).into()), + Some(Literal::from(3).into()), + ])) .into()], Interner::default(), ); @@ -49,11 +50,11 @@ fn check_numeric_array() { fn check_numeric_array_trailing() { check_parser( "[1, 2, 3,]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(2).into(), - Const::from(3).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(2).into()), + Some(Literal::from(3).into()), + ])) .into()], Interner::default(), ); @@ -64,12 +65,12 @@ fn check_numeric_array_trailing() { fn check_numeric_array_elision() { check_parser( "[1, 2, , 3]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(2).into(), - Node::Empty, - Const::from(3).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(2).into()), + None, + Some(Literal::from(3).into()), + ])) .into()], Interner::default(), ); @@ -80,13 +81,13 @@ fn check_numeric_array_elision() { fn check_numeric_array_repeated_elision() { check_parser( "[1, 2, ,, 3]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(2).into(), - Node::Empty, - Node::Empty, - Const::from(3).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(2).into()), + None, + None, + Some(Literal::from(3).into()), + ])) .into()], Interner::default(), ); @@ -98,11 +99,11 @@ fn check_combined() { let mut interner = Interner::default(); check_parser( "[1, \"a\", 2]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(interner.get_or_intern_static("a", utf16!("a"))).into(), - Const::from(2).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(interner.get_or_intern_static("a", utf16!("a"))).into()), + Some(Literal::from(2).into()), + ])) .into()], interner, ); @@ -113,11 +114,11 @@ fn check_combined() { fn check_combined_empty_str() { check_parser( "[1, \"\", 2]", - vec![ArrayDecl::from(vec![ - Const::from(1).into(), - Const::from(Sym::EMPTY_STRING).into(), - Const::from(2).into(), - ]) + vec![Expression::from(ArrayLiteral::from(vec![ + Some(Literal::from(1).into()), + Some(Literal::from(Sym::EMPTY_STRING).into()), + Some(Literal::from(2).into()), + ])) .into()], Interner::default(), ); 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 69baeae540b..6dcead96a74 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 @@ -3,14 +3,13 @@ mod tests; use crate::syntax::{ ast::{ - node::{function_contains_super, AsyncFunctionExpr}, - Keyword, Position, Punctuator, + function::function_contains_super, function::AsyncFunction, Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - AllowYield, Cursor, ParseError, TokenParser, + AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -49,13 +48,9 @@ impl TokenParser for AsyncFunctionExpression where R: Read, { - type Output = AsyncFunctionExpr; + type Output = AsyncFunction; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("AsyncFunctionExpression", "Parsing"); cursor.peek_expect_no_lineterminator(0, "async function expression", interner)?; cursor.expect( @@ -142,6 +137,6 @@ where ))); } - Ok(AsyncFunctionExpr::new(name, params, body)) + Ok(AsyncFunction::new(name, params, body)) } } 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 349dca5e20d..e2068b57131 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,17 +1,16 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - AsyncFunctionExpr, Declaration, DeclarationList, FormalParameterList, Return, - StatementList, - }, - Const, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + function::{AsyncFunction, FormalParameterList}, + statement::{ + declaration::{Declaration, DeclarationList}, + Return, }, - parser::tests::check_parser, }, + parser::tests::check_parser, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; +use boa_macros::utf16; /// Checks async expression parsing. #[test] @@ -24,13 +23,13 @@ fn check_async_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - add, + vec![Declaration::from_identifier( + add.into(), Some( - AsyncFunctionExpr::new::<_, _, StatementList>( + AsyncFunction::new( Some(add), FormalParameterList::default(), - vec![Return::new::<_, _, Option>(Const::from(1), None).into()].into(), + vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) .into(), ), @@ -55,24 +54,22 @@ fn check_nested_async_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - a, + vec![Declaration::from_identifier( + a.into(), Some( - AsyncFunctionExpr::new::<_, _, StatementList>( + AsyncFunction::new( Some(a), FormalParameterList::default(), vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - b, + vec![Declaration::from_identifier( + b.into(), Some( - AsyncFunctionExpr::new::<_, _, StatementList>( + AsyncFunction::new( Some(b), FormalParameterList::default(), - vec![Return::new::<_, _, Option>( - Const::from(1), - None, - ) - .into()] + vec![ + Return::new(Some(Literal::from(1).into()), None).into() + ] .into(), ) .into(), 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 ef616eeb971..4b436fc9429 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 @@ -12,14 +12,13 @@ mod tests; use crate::syntax::{ ast::{ - node::{function_contains_super, AsyncGeneratorExpr}, - Keyword, Position, Punctuator, + function::function_contains_super, function::AsyncGenerator, Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, TokenParser, + Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -52,13 +51,9 @@ where R: Read, { //The below needs to be implemented in ast::node - type Output = AsyncGeneratorExpr; + type Output = AsyncGenerator; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("AsyncGeneratorExpression", "Parsing"); cursor.peek_expect_no_lineterminator(0, "async generator expression", interner)?; @@ -175,6 +170,6 @@ where } //implement the below AsyncGeneratorExpr in ast::node - Ok(AsyncGeneratorExpr::new(name, params, body)) + Ok(AsyncGenerator::new(name, params, body)) } } 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 b8236d2b36f..d68791f26bb 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,17 +1,16 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - AsyncGeneratorExpr, Declaration, DeclarationList, FormalParameterList, Return, - StatementList, - }, - Const, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + function::{AsyncGenerator, FormalParameterList}, + statement::{ + declaration::{Declaration, DeclarationList}, + Return, }, - parser::tests::check_parser, }, + parser::tests::check_parser, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; +use boa_macros::utf16; ///checks async generator expression parsing @@ -25,13 +24,13 @@ fn check_async_generator_expr() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - add, + vec![Declaration::from_identifier( + add.into(), Some( - AsyncGeneratorExpr::new::<_, _, StatementList>( + AsyncGenerator::new( Some(add), FormalParameterList::default(), - vec![Return::new::<_, _, Option>(Const::from(1), None).into()].into(), + vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) .into(), ), @@ -56,24 +55,22 @@ fn check_nested_async_generator_expr() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - a, + vec![Declaration::from_identifier( + a.into(), Some( - AsyncGeneratorExpr::new::<_, _, StatementList>( + AsyncGenerator::new( Some(a), FormalParameterList::default(), vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - b, + vec![Declaration::from_identifier( + b.into(), Some( - AsyncGeneratorExpr::new::<_, _, StatementList>( + AsyncGenerator::new( Some(b), FormalParameterList::default(), - vec![Return::new::<_, _, Option>( - Const::from(1), - None, - ) - .into()] + vec![ + Return::new(Some(Literal::from(1).into()), None).into() + ] .into(), ) .into(), 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 84ad786e047..befb26de14d 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,9 +1,9 @@ use crate::syntax::{ - ast::{Keyword, Node}, + ast::{function::Class, Keyword}, lexer::TokenKind, parser::{ expression::BindingIdentifier, statement::ClassTail, AllowAwait, AllowYield, Cursor, - ParseError, TokenParser, + ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -43,13 +43,9 @@ impl TokenParser for ClassExpression where R: Read, { - type Output = Node; + type Output = Class; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ClassExpression", "Parsing"); let strict = cursor.strict_mode(); cursor.set_strict_mode(true); @@ -74,8 +70,6 @@ where }; cursor.set_strict_mode(strict); - Ok(Node::ClassExpr( - ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?, - )) + ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner) } } 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 7c3a3d36f2c..c5e104a4da5 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 @@ -12,14 +12,14 @@ mod tests; use crate::syntax::{ ast::{ - node::{function_contains_super, FunctionExpr}, + function::{function_contains_super, Function}, Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, TokenParser, + Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -53,13 +53,9 @@ impl TokenParser for FunctionExpression where R: Read, { - type Output = FunctionExpr; + type Output = Function; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FunctionExpression", "Parsing"); let name = match cursor @@ -136,6 +132,6 @@ where ))); } - Ok(FunctionExpr::new(name, params, body)) + Ok(Function::new(name, params, body)) } } 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 9d309999c15..c7e5acb0dea 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,17 +1,16 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - Declaration, DeclarationList, FormalParameterList, FunctionExpr, Return, - StatementList, - }, - Const, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + function::{FormalParameterList, Function}, + statement::{ + declaration::{Declaration, DeclarationList}, + Return, }, - parser::tests::check_parser, }, + parser::tests::check_parser, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; +use boa_macros::utf16; /// Checks async expression parsing. #[test] @@ -24,13 +23,13 @@ fn check_function_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - add, + vec![Declaration::from_identifier( + add.into(), Some( - FunctionExpr::new::<_, _, StatementList>( + Function::new( Some(add), FormalParameterList::default(), - vec![Return::new::<_, _, Option>(Const::from(1), None).into()].into(), + vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) .into(), ), @@ -55,24 +54,22 @@ fn check_nested_function_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - a, + vec![Declaration::from_identifier( + a.into(), Some( - FunctionExpr::new::<_, _, StatementList>( + Function::new( Some(a), FormalParameterList::default(), vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - b, + vec![Declaration::from_identifier( + b.into(), Some( - FunctionExpr::new::<_, _, StatementList>( + Function::new( Some(b), FormalParameterList::default(), - vec![Return::new::<_, _, Option>( - Const::from(1), - None, - ) - .into()] + vec![ + Return::new(Some(Literal::from(1).into()), None).into() + ] .into(), ) .into(), @@ -98,13 +95,13 @@ fn check_function_non_reserved_keyword() { macro_rules! genast { ($keyword:literal, $interner:expr) => { vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - $interner.get_or_intern_static("add", utf16!("add")), + vec![Declaration::from_identifier( + $interner.get_or_intern_static("add", utf16!("add")).into(), Some( - FunctionExpr::new::<_, _, StatementList>( + Function::new( Some($interner.get_or_intern_static($keyword, utf16!($keyword))), FormalParameterList::default(), - vec![Return::new::<_, _, Option>(Const::from(1), None).into()].into(), + vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) .into(), ), 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 3187dd7c3ad..e531a7d5900 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 @@ -12,14 +12,14 @@ mod tests; use crate::syntax::{ ast::{ - node::{function_contains_super, GeneratorExpr}, + function::{function_contains_super, Generator}, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - Cursor, ParseError, TokenParser, + Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -53,13 +53,9 @@ impl TokenParser for GeneratorExpression where R: Read, { - type Output = GeneratorExpr; + type Output = Generator; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("GeneratorExpression", "Parsing"); cursor.expect( @@ -138,6 +134,6 @@ where ))); } - Ok(GeneratorExpr::new(name, params, body)) + Ok(Generator::new(name, params, body)) } } 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 f3cff0e604d..d54010a88b0 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,17 +1,14 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - Declaration, DeclarationList, FormalParameterList, GeneratorExpr, StatementList, - Yield, - }, - Const, - }, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::{literal::Literal, Yield}, + function::{FormalParameterList, Generator}, + statement::declaration::{Declaration, DeclarationList}, + Expression, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn check_generator_function_expression() { @@ -23,13 +20,17 @@ fn check_generator_function_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - gen, + vec![Declaration::from_identifier( + gen.into(), Some( - GeneratorExpr::new::<_, _, StatementList>( + Generator::new( Some(gen), FormalParameterList::default(), - vec![Yield::new(Some(Const::from(1)), false).into()].into(), + vec![ + Expression::from(Yield::new(Some(Literal::from(1).into()), false)) + .into(), + ] + .into(), ) .into(), ), @@ -51,13 +52,17 @@ fn check_generator_function_delegate_yield_expression() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - gen, + vec![Declaration::from_identifier( + gen.into(), Some( - GeneratorExpr::new::<_, _, StatementList>( + Generator::new( Some(gen), FormalParameterList::default(), - vec![Yield::new(Some(Const::from(1)), true).into()].into(), + vec![ + Expression::from(Yield::new(Some(Literal::from(1).into()), true)) + .into(), + ] + .into(), ) .into(), ), diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs index 99af2f22bf5..2f944153f79 100644 --- a/boa_engine/src/syntax/parser/expression/primary/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs @@ -28,16 +28,22 @@ use self::{ }; use crate::syntax::{ ast::{ - node::{ - declaration::{BindingPatternTypeArray, BindingPatternTypeObject}, - operator::assign::{ - array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, AssignTarget, + self, + expression::{ + literal::Literal, + operator::{ + assign::{ + array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, + AssignTarget, + }, + binary::op::BinaryOp, }, - Call, Declaration, DeclarationPattern, FormalParameter, FormalParameterList, - Identifier, New, Node, + Call, Identifier, New, }, - op::BinOp, - Const, Keyword, Punctuator, Span, + function::{FormalParameter, FormalParameterList}, + pattern::{Pattern, PatternArrayElement, PatternObjectElement}, + statement::declaration::Declaration, + Keyword, Punctuator, Span, }, lexer::{token::Numeric, InputElement, Token, TokenKind}, parser::{ @@ -90,9 +96,9 @@ impl TokenParser for PrimaryExpression where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("PrimaryExpression", "Parsing"); // TODO: tok currently consumes the token instead of peeking, so the token @@ -107,7 +113,7 @@ where )), TokenKind::Keyword((Keyword::This, false)) => { cursor.next(interner).expect("token disappeared"); - Ok(Node::This) + Ok(ast::Expression::This) } TokenKind::Keyword((Keyword::Function, _)) => { cursor.next(interner).expect("token disappeared"); @@ -115,17 +121,18 @@ where if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) { GeneratorExpression::new(self.name) .parse(cursor, interner) - .map(Node::from) + .map(Into::into) } else { FunctionExpression::new(self.name) .parse(cursor, interner) - .map(Node::from) + .map(Into::into) } } TokenKind::Keyword((Keyword::Class, _)) => { cursor.next(interner).expect("token disappeared"); ClassExpression::new(self.name, self.allow_yield, self.allow_await) .parse(cursor, interner) + .map(Into::into) } TokenKind::Keyword((Keyword::Async, contain_escaped_char)) => { let contain_escaped_char = *contain_escaped_char; @@ -142,16 +149,16 @@ where Some(TokenKind::Punctuator(Punctuator::Mul)) => { AsyncGeneratorExpression::new(self.name) .parse(cursor, interner) - .map(Node::from) + .map(Into::into) } _ => AsyncFunctionExpression::new(self.name, self.allow_yield) .parse(cursor, interner) - .map(Node::from), + .map(Into::into), } } _ => IdentifierReference::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from), + .map(Into::into), } } TokenKind::Punctuator(Punctuator::OpenParen) => { @@ -170,23 +177,23 @@ where cursor.set_goal(InputElement::RegExp); ArrayLiteral::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::ArrayDecl) + .map(Into::into) } TokenKind::Punctuator(Punctuator::OpenBlock) => { cursor.next(interner).expect("token disappeared"); cursor.set_goal(InputElement::RegExp); - Ok(ObjectLiteral::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)? - .into()) + ObjectLiteral::new(self.allow_yield, self.allow_await) + .parse(cursor, interner) + .map(Into::into) } TokenKind::BooleanLiteral(boolean) => { - let node = Const::from(*boolean).into(); + let node = Literal::from(*boolean).into(); cursor.next(interner).expect("token disappeared"); Ok(node) } TokenKind::NullLiteral => { cursor.next(interner).expect("token disappeared"); - Ok(Const::Null.into()) + Ok(Literal::Null.into()) } TokenKind::Identifier(_) | TokenKind::Keyword(( @@ -194,14 +201,14 @@ where _, )) => IdentifierReference::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from), + .map(Into::into), TokenKind::StringLiteral(lit) => { - let node = Const::from(*lit).into(); + let node = Literal::from(*lit).into(); cursor.next(interner).expect("token disappeared"); Ok(node) } TokenKind::TemplateNoSubstitution(template_string) => { - let node = Const::from( + let node = Literal::from( template_string .to_owned_cooked(interner) .map_err(ParseError::lex)?, @@ -211,24 +218,24 @@ where Ok(node) } TokenKind::NumericLiteral(Numeric::Integer(num)) => { - let node = Const::from(*num).into(); + let node = Literal::from(*num).into(); cursor.next(interner).expect("token disappeared"); Ok(node) } TokenKind::NumericLiteral(Numeric::Rational(num)) => { - let node = Const::from(*num).into(); + let node = Literal::from(*num).into(); cursor.next(interner).expect("token disappeared"); Ok(node) } TokenKind::NumericLiteral(Numeric::BigInt(num)) => { - let node = Const::from(num.clone()).into(); + let node = Literal::from(num.clone()).into(); cursor.next(interner).expect("token disappeared"); Ok(node) } TokenKind::RegularExpressionLiteral(body, flags) => { - let node = Node::from(New::from(Call::new( - Identifier::new(Sym::REGEXP), - vec![Const::from(*body).into(), Const::from(*flags).into()], + let node = ast::Expression::from(New::from(Call::new( + Identifier::new(Sym::REGEXP).into(), + vec![Literal::from(*body).into(), Literal::from(*flags).into()].into(), ))); cursor.next(interner).expect("token disappeared"); Ok(node) @@ -239,9 +246,9 @@ where let tok = cursor.lex_regex(position, interner)?; if let TokenKind::RegularExpressionLiteral(body, flags) = *tok.kind() { - Ok(Node::from(New::from(Call::new( - Identifier::new(Sym::REGEXP), - vec![Const::from(body).into(), Const::from(flags).into()], + Ok(ast::Expression::from(New::from(Call::new( + Identifier::new(Sym::REGEXP).into(), + vec![Literal::from(body).into(), Literal::from(flags).into()].into(), )))) } else { // A regex was expected and nothing else. @@ -262,7 +269,7 @@ where .map_err(ParseError::lex)?, ); cursor.next(interner).expect("token disappeared"); - parser.parse(cursor, interner).map(Node::TemplateLit) + parser.parse(cursor, interner).map(Into::into) } _ => Err(ParseError::unexpected( tok.to_string(interner), @@ -306,14 +313,14 @@ impl TokenParser for CoverParenthesizedExpressionAndArrowParameterList where R: Read, { - type Output = Node; + type Output = ast::Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { #[derive(Debug)] enum InnerExpression { - Expression(Node), - SpreadObject(Vec), - SpreadArray(Vec), + Expression(ast::Expression), + SpreadObject(Vec), + SpreadArray(Vec), SpreadBinding(Sym), } @@ -430,7 +437,7 @@ where for expression in expressions { match expression { InnerExpression::Expression(node) => { - node_to_formal_parameters( + expression_to_formal_parameters( &node, &mut parameters, cursor.strict_mode(), @@ -438,17 +445,17 @@ where )?; } InnerExpression::SpreadObject(bindings) => { - let declaration = Declaration::new_with_object_pattern(bindings, None); + let declaration = Declaration::from_pattern(bindings.into(), None); let parameter = FormalParameter::new(declaration, true); parameters.push(parameter); } InnerExpression::SpreadArray(bindings) => { - let declaration = Declaration::new_with_array_pattern(bindings, None); + let declaration = Declaration::from_pattern(bindings.into(), None); let parameter = FormalParameter::new(declaration, true); parameters.push(parameter); } InnerExpression::SpreadBinding(ident) => { - let declaration = Declaration::new_with_identifier(ident, None); + let declaration = Declaration::from_identifier(ident.into(), None); let parameter = FormalParameter::new(declaration, true); parameters.push(parameter); } @@ -473,61 +480,61 @@ where )); } - Ok(Node::FormalParameterList(parameters)) + Ok(ast::Expression::FormalParameterList(parameters)) } } -/// Convert a node to a formal parameter and append it to the given parameter list. -fn node_to_formal_parameters( - node: &Node, +/// Convert an expression to a formal parameter and append it to the given parameter list. +fn expression_to_formal_parameters( + node: &ast::Expression, parameters: &mut Vec, strict: bool, span: Span, ) -> Result<(), ParseError> { match node { - Node::Identifier(identifier) if strict && identifier.sym() == Sym::EVAL => { + ast::Expression::Identifier(identifier) if strict && identifier.sym() == Sym::EVAL => { return Err(ParseError::general( "parameter name 'eval' not allowed in strict mode", span.start(), )); } - Node::Identifier(identifier) if strict && identifier.sym() == Sym::ARGUMENTS => { + ast::Expression::Identifier(identifier) if strict && identifier.sym() == Sym::ARGUMENTS => { return Err(ParseError::general( "parameter name 'arguments' not allowed in strict mode", span.start(), )); } - Node::Identifier(identifier) => { + ast::Expression::Identifier(identifier) => { parameters.push(FormalParameter::new( - Declaration::new_with_identifier(identifier.sym(), None), + Declaration::from_identifier(*identifier, None), false, )); } - Node::BinOp(bin_op) if bin_op.op() == BinOp::Comma => { - node_to_formal_parameters(bin_op.lhs(), parameters, strict, span)?; - node_to_formal_parameters(bin_op.rhs(), parameters, strict, span)?; + ast::Expression::Binary(bin_op) if bin_op.op() == BinaryOp::Comma => { + expression_to_formal_parameters(bin_op.lhs(), parameters, strict, span)?; + expression_to_formal_parameters(bin_op.rhs(), parameters, strict, span)?; } - Node::Assign(assign) => match assign.lhs() { + ast::Expression::Assign(assign) => match assign.lhs() { AssignTarget::Identifier(ident) => { parameters.push(FormalParameter::new( - Declaration::new_with_identifier(ident.sym(), Some(assign.rhs().clone())), + Declaration::from_identifier(*ident, Some(assign.rhs().clone())), false, )); } - AssignTarget::DeclarationPattern(pattern) => match pattern { - DeclarationPattern::Object(pattern) => { + AssignTarget::Pattern(pattern) => match pattern { + Pattern::Object(pattern) => { parameters.push(FormalParameter::new( - Declaration::new_with_object_pattern( - pattern.bindings().clone(), + Declaration::from_pattern( + pattern.bindings().to_vec().into(), Some(assign.rhs().clone()), ), false, )); } - DeclarationPattern::Array(pattern) => { + Pattern::Array(pattern) => { parameters.push(FormalParameter::new( - Declaration::new_with_array_pattern( - pattern.bindings().clone(), + Declaration::from_pattern( + pattern.bindings().to_vec().into(), Some(assign.rhs().clone()), ), false, @@ -541,11 +548,14 @@ fn node_to_formal_parameters( )); } }, - Node::Object(object) => { + ast::Expression::ObjectLiteral(object) => { let decl = object_decl_to_declaration_pattern(object, strict); if let Some(pattern) = decl { - parameters.push(FormalParameter::new(Declaration::Pattern(pattern), false)); + parameters.push(FormalParameter::new( + Declaration::from_pattern(pattern.into(), None), + false, + )); } else { return Err(ParseError::general( "invalid object binding pattern in formal parameter list", @@ -553,11 +563,14 @@ fn node_to_formal_parameters( )); } } - Node::ArrayDecl(array) => { + ast::Expression::ArrayLiteral(array) => { let decl = array_decl_to_declaration_pattern(array, strict); if let Some(pattern) = decl { - parameters.push(FormalParameter::new(Declaration::Pattern(pattern), false)); + parameters.push(FormalParameter::new( + Declaration::from_pattern(pattern.into(), None), + false, + )); } else { return Err(ParseError::general( "invalid array binding pattern in formal parameter list", 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 a5ae2a11c59..31e0dd328ff 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 @@ -10,27 +10,23 @@ #[cfg(test)] mod tests; -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - function_contains_super, has_direct_super, - object::{self, MethodDefinition}, - AsyncFunctionExpr, AsyncGeneratorExpr, FormalParameterList, FunctionExpr, - GeneratorExpr, Node, Object, - }, - Const, 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, - }, +use crate::syntax::{ + ast::{ + expression::literal::{self, Literal}, + 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, }, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; use boa_profiler::Profiler; use std::io::Read; @@ -66,13 +62,9 @@ impl TokenParser for ObjectLiteral where R: Read, { - type Output = Object; + type Output = literal::ObjectLiteral; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ObjectLiteral", "Parsing"); let mut elements = Vec::new(); @@ -101,7 +93,7 @@ where } } - Ok(Object::from(elements)) + Ok(literal::ObjectLiteral::from(elements)) } } @@ -135,13 +127,9 @@ impl TokenParser for PropertyDefinition where R: Read, { - type Output = object::PropertyDefinition; + type Output = property::PropertyDefinition; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("PropertyDefinition", "Parsing"); match cursor @@ -152,7 +140,10 @@ where TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma) => { let ident = IdentifierReference::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - return Ok(object::PropertyDefinition::property(ident.sym(), ident)); + return Ok(property::PropertyDefinition::Property( + ident.sym().into(), + ident.into(), + )); } TokenKind::Punctuator(Punctuator::Assign) => { return CoverInitializedName::new(self.allow_yield, self.allow_await) @@ -165,7 +156,7 @@ where if cursor.next_if(Punctuator::Spread, interner)?.is_some() { let node = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - return Ok(object::PropertyDefinition::SpreadObject(node)); + return Ok(property::PropertyDefinition::SpreadObject(node)); } //Async [AsyncMethod, AsyncGeneratorMethod] object methods @@ -202,7 +193,7 @@ where } let property_name = - if let object::ClassElementName::PropertyName(property_name) = + if let property::ClassElementName::PropertyName(property_name) = class_element_name { property_name @@ -213,7 +204,7 @@ where )); }; - return Ok(object::PropertyDefinition::method_definition( + return Ok(property::PropertyDefinition::MethodDefinition( method, property_name, )); @@ -221,7 +212,7 @@ where let (class_element_name, method) = AsyncMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; - let property_name = if let object::ClassElementName::PropertyName(property_name) = + let property_name = if let property::ClassElementName::PropertyName(property_name) = class_element_name { property_name @@ -237,7 +228,7 @@ where return Err(ParseError::general("invalid super usage", position)); } - return Ok(object::PropertyDefinition::method_definition( + return Ok(property::PropertyDefinition::MethodDefinition( method, property_name, )); @@ -265,13 +256,13 @@ where } match class_element_name { - object::ClassElementName::PropertyName(property_name) => { - return Ok(object::PropertyDefinition::method_definition( + property::ClassElementName::PropertyName(property_name) => { + return Ok(property::PropertyDefinition::MethodDefinition( method, property_name, )) } - object::ClassElementName::PrivateIdentifier(_) => { + property::ClassElementName::PrivateIdentifier(_) => { return Err(ParseError::general( "private identifier not allowed in object literal", position, @@ -287,7 +278,7 @@ where if cursor.next_if(Punctuator::Colon, interner)?.is_some() { let value = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - return Ok(object::PropertyDefinition::property(property_name, value)); + return Ok(property::PropertyDefinition::Property(property_name, value)); } let ordinary_method = cursor @@ -298,7 +289,7 @@ where match property_name { // MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] } - object::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => { + property::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => { let position = cursor .peek(0, interner)? .ok_or(ParseError::AbruptEnd)? @@ -336,8 +327,8 @@ where return Err(ParseError::general("invalid super usage", position)); } - Ok(object::PropertyDefinition::method_definition( - MethodDefinition::Get(FunctionExpr::new( + Ok(property::PropertyDefinition::MethodDefinition( + MethodDefinition::Get(Function::new( None, FormalParameterList::default(), body, @@ -346,7 +337,7 @@ where )) } // MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] } - object::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => { + property::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => { property_name = PropertyName::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; @@ -397,8 +388,8 @@ where )); } - Ok(object::PropertyDefinition::method_definition( - MethodDefinition::Set(FunctionExpr::new(None, parameters, body)), + Ok(property::PropertyDefinition::MethodDefinition( + MethodDefinition::Set(Function::new(None, parameters, body)), property_name, )) } @@ -476,8 +467,8 @@ where )); } - Ok(object::PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new(None, params, body)), + Ok(property::PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new(None, params, body)), property_name, )) } @@ -515,13 +506,9 @@ impl TokenParser for PropertyName where R: Read, { - type Output = object::PropertyName; + type Output = property::PropertyName; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("PropertyName", "Parsing"); let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -534,27 +521,20 @@ where cursor.expect(Punctuator::CloseBracket, "expected token ']'", interner)?; return Ok(node.into()); } - TokenKind::Identifier(name) => object::PropertyName::Literal(*name), - TokenKind::StringLiteral(name) => Node::Const(Const::from(*name)).into(), + TokenKind::Identifier(name) | TokenKind::StringLiteral(name) => (*name).into(), TokenKind::NumericLiteral(num) => match num { - Numeric::Rational(num) => Node::Const(Const::from(*num)).into(), - Numeric::Integer(num) => Node::Const(Const::from(*num)).into(), - Numeric::BigInt(num) => Node::Const(Const::from(num.clone())).into(), + Numeric::Rational(num) => Expression::Literal(Literal::from(*num)).into(), + Numeric::Integer(num) => Expression::Literal(Literal::from(*num)).into(), + Numeric::BigInt(num) => Expression::Literal(Literal::from(num.clone())).into(), }, TokenKind::Keyword((word, _)) => { let (utf8, utf16) = word.as_str(); - Node::Const(Const::from(interner.get_or_intern_static(utf8, utf16))).into() + interner.get_or_intern_static(utf8, utf16).into() } - TokenKind::NullLiteral => Node::Const(Const::from(Sym::NULL)).into(), + TokenKind::NullLiteral => (Sym::NULL).into(), TokenKind::BooleanLiteral(bool) => match bool { - true => Node::Const(Const::from( - interner.get_or_intern_static("true", utf16!("true")), - )) - .into(), - false => Node::Const(Const::from( - interner.get_or_intern_static("false", utf16!("false")), - )) - .into(), + true => (interner.get_or_intern_static("true", utf16!("true"))).into(), + false => (interner.get_or_intern_static("false", utf16!("false"))).into(), }, _ => return Err(ParseError::AbruptEnd), }; @@ -593,13 +573,9 @@ impl TokenParser for ClassElementName where R: Read, { - type Output = object::ClassElementName; + type Output = property::ClassElementName; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ClassElementName", "Parsing"); match cursor @@ -610,9 +586,9 @@ where TokenKind::PrivateIdentifier(ident) => { let ident = *ident; cursor.next(interner).expect("token disappeared"); - Ok(object::ClassElementName::PrivateIdentifier(ident)) + Ok(property::ClassElementName::PrivateIdentifier(ident)) } - _ => Ok(object::ClassElementName::PropertyName( + _ => Ok(property::ClassElementName::PropertyName( PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?, )), } @@ -660,9 +636,9 @@ impl TokenParser for Initializer where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Initializer", "Parsing"); cursor.expect(Punctuator::Assign, "initializer", interner)?; @@ -701,13 +677,9 @@ impl TokenParser for GeneratorMethod where R: Read, { - type Output = (object::ClassElementName, MethodDefinition); + type Output = (property::ClassElementName, MethodDefinition); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("GeneratorMethod", "Parsing"); cursor.expect(Punctuator::Mul, "generator method definition", interner)?; @@ -756,7 +728,7 @@ where Ok(( class_element_name, - MethodDefinition::Generator(GeneratorExpr::new(None, params, body)), + MethodDefinition::Generator(Generator::new(None, params, body)), )) } } @@ -791,13 +763,9 @@ impl TokenParser for AsyncGeneratorMethod where R: Read, { - type Output = (object::ClassElementName, MethodDefinition); + type Output = (property::ClassElementName, MethodDefinition); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("AsyncGeneratorMethod", "Parsing"); cursor.expect( Punctuator::Mul, @@ -874,7 +842,7 @@ where Ok(( name, - MethodDefinition::AsyncGenerator(AsyncGeneratorExpr::new(None, params, body)), + MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body)), )) } } @@ -909,13 +877,9 @@ impl TokenParser for AsyncMethod where R: Read, { - type Output = (object::ClassElementName, MethodDefinition); + type Output = (property::ClassElementName, MethodDefinition); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("AsyncMethod", "Parsing"); let class_element_name = @@ -963,7 +927,7 @@ where Ok(( class_element_name, - MethodDefinition::Async(AsyncFunctionExpr::new(None, params, body)), + MethodDefinition::Async(AsyncFunction::new(None, params, body)), )) } } @@ -998,13 +962,9 @@ impl TokenParser for CoverInitializedName where R: Read, { - type Output = object::PropertyDefinition; + type Output = property::PropertyDefinition; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("CoverInitializedName", "Parsing"); let ident = @@ -1015,7 +975,7 @@ where let expr = AssignmentExpression::new(ident.sym(), true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - Ok(object::PropertyDefinition::CoverInitializedName( + Ok(property::PropertyDefinition::CoverInitializedName( ident.sym(), expr, )) 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 028323c7193..ebc7eebb4c4 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,19 +1,23 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - object::{MethodDefinition, PropertyDefinition}, - AsyncFunctionExpr, AsyncGeneratorExpr, Declaration, DeclarationList, - FormalParameter, FormalParameterList, FormalParameterListFlags, FunctionExpr, - Identifier, Node, Object, - }, - Const, +use crate::syntax::{ + ast::{ + expression::{ + literal::{Literal, ObjectLiteral}, + Identifier, + }, + function::{ + AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList, + FormalParameterListFlags, Function, + }, + property::{MethodDefinition, PropertyDefinition, PropertyName}, + statement::{ + declaration::{Declaration, DeclarationList}, + StatementList, }, - parser::tests::{check_invalid, check_parser}, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::Interner; +use boa_macros::utf16; /// Checks object literal parsing. #[test] @@ -21,13 +25,13 @@ fn check_object_literal() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(true), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(true).into(), ), - PropertyDefinition::property( - interner.get_or_intern_static("b", utf16!("b")), - Const::from(false), + PropertyDefinition::Property( + interner.get_or_intern_static("b", utf16!("b")).into(), + Literal::from(false).into(), ), ]; @@ -38,9 +42,9 @@ fn check_object_literal() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -55,17 +59,17 @@ fn check_object_short_function() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(true), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(true).into(), ), - PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new( + PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")), + interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -76,9 +80,9 @@ fn check_object_short_function() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -93,17 +97,17 @@ fn check_object_short_function_arguments() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(true), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(true).into(), ), - PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new( + PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new( None, FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("test", utf16!("test")), + Declaration::from_identifier( + interner.get_or_intern_static("test", utf16!("test")).into(), None, ), false, @@ -111,9 +115,9 @@ fn check_object_short_function_arguments() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![], + StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")), + interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -124,9 +128,9 @@ fn check_object_short_function_arguments() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -140,17 +144,17 @@ fn check_object_getter() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(true), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(true).into(), ), - PropertyDefinition::method_definition( - MethodDefinition::Get(FunctionExpr::new( + PropertyDefinition::MethodDefinition( + MethodDefinition::Get(Function::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")), + interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -161,9 +165,9 @@ fn check_object_getter() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -177,17 +181,17 @@ fn check_object_setter() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(true), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(true).into(), ), - PropertyDefinition::method_definition( - MethodDefinition::Set(FunctionExpr::new( + PropertyDefinition::MethodDefinition( + MethodDefinition::Set(Function::new( None, FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("test", utf16!("test")), + Declaration::from_identifier( + interner.get_or_intern_static("test", utf16!("test")).into(), None, ), false, @@ -195,9 +199,9 @@ fn check_object_setter() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![], + StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")), + interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -208,9 +212,9 @@ fn check_object_setter() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -223,13 +227,13 @@ fn check_object_setter() { fn check_object_short_function_get() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new( + let object_properties = vec![PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("get", utf16!("get")), + interner.get_or_intern_static("get", utf16!("get")).into(), )]; check_parser( @@ -238,9 +242,9 @@ fn check_object_short_function_get() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -253,13 +257,13 @@ fn check_object_short_function_get() { fn check_object_short_function_set() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new( + let object_properties = vec![PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("set", utf16!("set")), + interner.get_or_intern_static("set", utf16!("set")).into(), )]; check_parser( @@ -268,9 +272,9 @@ fn check_object_short_function_set() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -283,9 +287,9 @@ fn check_object_short_function_set() { fn check_object_shorthand_property_names() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), + let object_properties = vec![PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), )]; check_parser( @@ -294,17 +298,17 @@ fn check_object_shorthand_property_names() { ", vec![ DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::from(true).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(true).into()), )] .into(), ) .into(), DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -319,13 +323,13 @@ fn check_object_shorthand_multiple_properties() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), ), - PropertyDefinition::property( - interner.get_or_intern_static("b", utf16!("b")), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), + PropertyDefinition::Property( + interner.get_or_intern_static("b", utf16!("b")).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), ), ]; @@ -336,25 +340,25 @@ fn check_object_shorthand_multiple_properties() { ", vec![ DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::from(true).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(true).into()), )] .into(), ) .into(), DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), - Some(Const::from(false).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), + Some(Literal::from(false).into()), )] .into(), ) .into(), DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -369,22 +373,22 @@ fn check_object_spread() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(1), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(1).into(), + ), + PropertyDefinition::SpreadObject( + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), ), - PropertyDefinition::spread_object(Identifier::new( - interner.get_or_intern_static("b", utf16!("b")), - )), ]; check_parser( "const x = { a: 1, ...b }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -397,13 +401,13 @@ fn check_object_spread() { fn check_async_method() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::method_definition( - MethodDefinition::Async(AsyncFunctionExpr::new( + let object_properties = vec![PropertyDefinition::MethodDefinition( + MethodDefinition::Async(AsyncFunction::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("dive", utf16!("dive")), + interner.get_or_intern_static("dive", utf16!("dive")).into(), )]; check_parser( @@ -412,9 +416,9 @@ fn check_async_method() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -427,13 +431,15 @@ fn check_async_method() { fn check_async_generator_method() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::method_definition( - MethodDefinition::AsyncGenerator(AsyncGeneratorExpr::new( + let object_properties = vec![PropertyDefinition::MethodDefinition( + MethodDefinition::AsyncGenerator(AsyncGenerator::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), - interner.get_or_intern_static("vroom", utf16!("vroom")), + interner + .get_or_intern_static("vroom", utf16!("vroom")) + .into(), )]; check_parser( @@ -442,9 +448,9 @@ fn check_async_generator_method() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -479,15 +485,13 @@ fn check_async_gen_method_lineterminator() { fn check_async_ordinary_method() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::method_definition( - MethodDefinition::Ordinary(FunctionExpr::new( + let object_properties = vec![PropertyDefinition::MethodDefinition( + MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), - vec![], - )), - Node::Const(Const::from( - interner.get_or_intern_static("async", utf16!("async")), + StatementList::default(), )), + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), )]; check_parser( @@ -496,9 +500,9 @@ fn check_async_ordinary_method() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -511,11 +515,9 @@ fn check_async_ordinary_method() { fn check_async_property() { let mut interner = Interner::default(); - let object_properties = vec![PropertyDefinition::property( - Node::Const(Const::from( - interner.get_or_intern_static("async", utf16!("async")), - )), - Const::from(true), + let object_properties = vec![PropertyDefinition::Property( + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), + Literal::from(true).into(), )]; check_parser( @@ -524,9 +526,9 @@ fn check_async_property() { }; ", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) 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 e1760d62c6f..807faad5cd2 100644 --- a/boa_engine/src/syntax/parser/expression/primary/template/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/template/mod.rs @@ -9,11 +9,14 @@ use crate::syntax::{ ast::{ - node::template::{TemplateElement, TemplateLit}, + expression::literal::{self, TemplateElement}, Position, Punctuator, }, lexer::TokenKind, - parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser}, + parser::{ + expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, + TokenParser, + }, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -55,13 +58,9 @@ impl TokenParser for TemplateLiteral where R: Read, { - type Output = TemplateLit; + type Output = literal::TemplateLiteral; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("TemplateLiteral", "Parsing"); let mut elements = vec![ @@ -101,7 +100,7 @@ where .map_err(ParseError::lex)?; elements.push(TemplateElement::String(cooked)); - return Ok(TemplateLit::new(elements)); + return Ok(literal::TemplateLiteral::new(elements.into())); } _ => { return Err(ParseError::general( diff --git a/boa_engine/src/syntax/parser/expression/primary/tests.rs b/boa_engine/src/syntax/parser/expression/primary/tests.rs index a754fc196a5..9c2202dfb8e 100644 --- a/boa_engine/src/syntax/parser/expression/primary/tests.rs +++ b/boa_engine/src/syntax/parser/expression/primary/tests.rs @@ -1,15 +1,16 @@ -use crate::{ - string::utf16, - syntax::{ast::Const, parser::tests::check_parser}, +use crate::syntax::{ + ast::{expression::literal::Literal, Expression}, + parser::tests::check_parser, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; #[test] fn check_string() { // Check empty string check_parser( "\"\"", - vec![Const::from(Sym::EMPTY_STRING).into()], + vec![Expression::from(Literal::from(Sym::EMPTY_STRING)).into()], Interner::default(), ); @@ -17,7 +18,10 @@ fn check_string() { let mut interner = Interner::default(); check_parser( "\"hello\"", - vec![Const::from(interner.get_or_intern_static("hello", utf16!("hello"))).into()], + vec![Expression::from(Literal::from( + interner.get_or_intern_static("hello", utf16!("hello")), + )) + .into()], interner, ); } diff --git a/boa_engine/src/syntax/parser/expression/tests.rs b/boa_engine/src/syntax/parser/expression/tests.rs index 56351e6d59f..b30e33ad8b0 100644 --- a/boa_engine/src/syntax/parser/expression/tests.rs +++ b/boa_engine/src/syntax/parser/expression/tests.rs @@ -1,15 +1,21 @@ -use crate::{ - string::utf16, - syntax::{ - ast::op::{AssignOp, BitOp, CompOp, LogOp, NumOp}, - ast::{ - node::{BinOp, Call, Declaration, DeclarationList, Identifier, New}, - Const, Node, +use crate::syntax::{ + ast::{ + expression::{ + literal::Literal, + operator::{ + assign::op::AssignOp, + binary::op::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp}, + Assign, Binary, + }, + Call, Identifier, New, }, - parser::tests::{check_invalid, check_parser}, + statement::declaration::{Declaration, DeclarationList}, + Expression, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; /// Checks numeric operations #[test] @@ -17,11 +23,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a + b", - vec![BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -29,11 +35,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a+1", - vec![BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(1), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(1).into(), + )) .into()], interner, ); @@ -41,11 +47,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a - b", - vec![BinOp::new( - NumOp::Sub, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Sub.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -53,11 +59,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a-1", - vec![BinOp::new( - NumOp::Sub, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(1), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Sub.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(1).into(), + )) .into()], interner, ); @@ -65,11 +71,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a / b", - vec![BinOp::new( - NumOp::Div, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Div.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -77,11 +83,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a/2", - vec![BinOp::new( - NumOp::Div, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(2), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Div.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(2).into(), + )) .into()], interner, ); @@ -90,15 +96,21 @@ fn check_numeric_operations() { check_parser( "let myRegex = /=/;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("myRegex", utf16!("myRegex")), - Node::from(New::from(Call::new( - Identifier::new(Sym::REGEXP), - vec![ - Node::from(Const::from(interner.get_or_intern_static("=", utf16!("=")))), - Node::from(Const::from(Sym::EMPTY_STRING)), - ], - ))), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("myRegex", utf16!("myRegex")) + .into(), + Some( + New::from(Call::new( + Identifier::new(Sym::REGEXP).into(), + vec![ + Literal::from(interner.get_or_intern_static("=", utf16!("="))).into(), + Literal::from(Sym::EMPTY_STRING).into(), + ] + .into(), + )) + .into(), + ), )] .into(), ) @@ -109,11 +121,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a * b", - vec![BinOp::new( - NumOp::Mul, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Mul.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -121,11 +133,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a*2", - vec![BinOp::new( - NumOp::Mul, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(2), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Mul.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(2).into(), + )) .into()], interner, ); @@ -133,11 +145,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a ** b", - vec![BinOp::new( - NumOp::Exp, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Exp.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -145,11 +157,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a**2", - vec![BinOp::new( - NumOp::Exp, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(2), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Exp.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(2).into(), + )) .into()], interner, ); @@ -157,11 +169,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a % b", - vec![BinOp::new( - NumOp::Mod, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Mod.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -169,11 +181,11 @@ fn check_numeric_operations() { let mut interner = Interner::default(); check_parser( "a%2", - vec![BinOp::new( - NumOp::Mod, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(2), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Mod.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(2).into(), + )) .into()], interner, ); @@ -185,23 +197,26 @@ fn check_complex_numeric_operations() { let mut interner = Interner::default(); check_parser( "a + d*(b-3)+1", - vec![BinOp::new( - NumOp::Add, - BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - BinOp::new( - NumOp::Mul, - Identifier::new(interner.get_or_intern_static("d", utf16!("d"))), - BinOp::new( - NumOp::Sub, - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - Const::from(3), - ), - ), - ), - Const::from(1), - ) + vec![Expression::from(Binary::new( + ArithmeticOp::Add.into(), + Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Binary::new( + ArithmeticOp::Mul.into(), + Identifier::new(interner.get_or_intern_static("d", utf16!("d"))).into(), + Binary::new( + ArithmeticOp::Sub.into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + Literal::from(3).into(), + ) + .into(), + ) + .into(), + ) + .into(), + Literal::from(1).into(), + )) .into()], interner, ); @@ -213,11 +228,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a & b", - vec![BinOp::new( - BitOp::And, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::And.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -225,11 +240,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a&b", - vec![BinOp::new( - BitOp::And, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::And.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -237,11 +252,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a | b", - vec![BinOp::new( - BitOp::Or, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Or.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -249,11 +264,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a|b", - vec![BinOp::new( - BitOp::Or, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Or.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -261,11 +276,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a ^ b", - vec![BinOp::new( - BitOp::Xor, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Xor.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -273,11 +288,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a^b", - vec![BinOp::new( - BitOp::Xor, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Xor.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -285,11 +300,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a << b", - vec![BinOp::new( - BitOp::Shl, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Shl.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -297,11 +312,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a<> b", - vec![BinOp::new( - BitOp::Shr, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Shr.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -321,11 +336,11 @@ fn check_bitwise_operations() { let mut interner = Interner::default(); check_parser( "a>>b", - vec![BinOp::new( - BitOp::Shr, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + BitwiseOp::Shr.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -337,11 +352,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a += b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -349,11 +364,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a -= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Sub, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -361,11 +376,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a *= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Mul, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -373,11 +388,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a **= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Exp, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -385,11 +400,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a /= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Div, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -397,11 +412,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a %= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Mod, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -409,11 +424,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a &= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::And, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -421,11 +436,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a |= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Or, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -433,11 +448,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a ^= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Xor, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -445,11 +460,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a <<= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Shl, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -457,11 +472,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a >>= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Shr, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -469,11 +484,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a >>>= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Ushr, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -481,11 +496,16 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a %= 10 / 2", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Mod, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - BinOp::new(NumOp::Div, Const::from(10), Const::from(2)), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Binary::new( + ArithmeticOp::Div.into(), + Literal::from(10).into(), + Literal::from(2).into(), + ) + .into(), + )) .into()], interner, ); @@ -493,11 +513,11 @@ fn check_assign_operations() { let mut interner = Interner::default(); check_parser( "a ??= b", - vec![BinOp::new( + vec![Expression::from(Assign::new( AssignOp::Coalesce, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -508,11 +528,11 @@ fn check_relational_operations() { let mut interner = Interner::default(); check_parser( "a < b", - vec![BinOp::new( - CompOp::LessThan, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + RelationalOp::LessThan.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -520,11 +540,11 @@ fn check_relational_operations() { let mut interner = Interner::default(); check_parser( "a > b", - vec![BinOp::new( - CompOp::GreaterThan, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + RelationalOp::GreaterThan.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -532,11 +552,11 @@ fn check_relational_operations() { let mut interner = Interner::default(); check_parser( "a <= b", - vec![BinOp::new( - CompOp::LessThanOrEqual, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + RelationalOp::LessThanOrEqual.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -544,11 +564,11 @@ fn check_relational_operations() { let mut interner = Interner::default(); check_parser( "a >= b", - vec![BinOp::new( - CompOp::GreaterThanOrEqual, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ) + vec![Expression::from(Binary::new( + RelationalOp::GreaterThanOrEqual.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + )) .into()], interner, ); @@ -556,11 +576,11 @@ fn check_relational_operations() { let mut interner = Interner::default(); check_parser( "p in o", - vec![BinOp::new( - CompOp::In, - Identifier::new(interner.get_or_intern_static("p", utf16!("p"))), - Identifier::new(interner.get_or_intern_static("o", utf16!("o"))), - ) + vec![Expression::from(Binary::new( + RelationalOp::In.into(), + Identifier::new(interner.get_or_intern_static("p", utf16!("p"))).into(), + Identifier::new(interner.get_or_intern_static("o", utf16!("o"))).into(), + )) .into()], interner, ); @@ -571,23 +591,26 @@ fn check_logical_expressions() { let mut interner = Interner::default(); check_parser( "a && b || c && d || e", - vec![BinOp::new( - LogOp::Or, - BinOp::new( - LogOp::And, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ), - BinOp::new( - LogOp::Or, - BinOp::new( - LogOp::And, - Identifier::new(interner.get_or_intern_static("c", utf16!("c"))), - Identifier::new(interner.get_or_intern_static("d", utf16!("d"))), - ), - Identifier::new(interner.get_or_intern_static("e", utf16!("e"))), - ), - ) + vec![Expression::from(Binary::new( + LogicalOp::Or.into(), + Binary::new( + LogicalOp::And.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), + Binary::new( + LogicalOp::Or.into(), + Binary::new( + LogicalOp::And.into(), + Identifier::new(interner.get_or_intern_static("c", utf16!("c"))).into(), + Identifier::new(interner.get_or_intern_static("d", utf16!("d"))).into(), + ) + .into(), + Identifier::new(interner.get_or_intern_static("e", utf16!("e"))).into(), + ) + .into(), + )) .into()], interner, ); @@ -595,15 +618,16 @@ fn check_logical_expressions() { let mut interner = Interner::default(); check_parser( "a ?? b ?? c", - vec![BinOp::new( - LogOp::Coalesce, - BinOp::new( - LogOp::Coalesce, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ), - Identifier::new(interner.get_or_intern_static("c", utf16!("c"))), - ) + vec![Expression::from(Binary::new( + LogicalOp::Coalesce.into(), + Binary::new( + LogicalOp::Coalesce.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), + Identifier::new(interner.get_or_intern_static("c", utf16!("c"))).into(), + )) .into()], interner, ); @@ -619,7 +643,10 @@ macro_rules! check_non_reserved_identifier { let mut interner = Interner::default(); check_parser( format!("({})", $keyword).as_str(), - vec![Identifier::new(interner.get_or_intern_static($keyword, utf16!($keyword))).into()], + vec![Expression::from(Identifier::new( + interner.get_or_intern_static($keyword, utf16!($keyword)), + )) + .into()], interner, ); }}; diff --git a/boa_engine/src/syntax/parser/expression/unary.rs b/boa_engine/src/syntax/parser/expression/unary.rs index eb1ef9389da..958f40f9909 100644 --- a/boa_engine/src/syntax/parser/expression/unary.rs +++ b/boa_engine/src/syntax/parser/expression/unary.rs @@ -9,9 +9,8 @@ use crate::syntax::{ ast::{ - node::{self, Node}, - op::UnaryOp, - Keyword, Punctuator, + expression::operator::{unary::op::UnaryOp, Unary}, + Expression, Keyword, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ @@ -58,9 +57,9 @@ impl TokenParser for UnaryExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("UnaryExpression", "Parsing"); let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -79,13 +78,13 @@ where let val = self.parse(cursor, interner)?; match val { - Node::Identifier(_) if cursor.strict_mode() => { + Expression::Identifier(_) if cursor.strict_mode() => { return Err(ParseError::lex(LexError::Syntax( "Delete statements not allowed in strict mode".into(), token_start, ))); } - Node::GetPrivateField(_) => { + Expression::PrivatePropertyAccess(_) => { return Err(ParseError::general( "private fields can not be deleted", position, @@ -94,31 +93,31 @@ where _ => {} } - Ok(node::UnaryOp::new(UnaryOp::Delete, val).into()) + Ok(Unary::new(UnaryOp::Delete, val).into()) } TokenKind::Keyword((Keyword::Void, false)) => { cursor.next(interner)?.expect("Void keyword vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::Void, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::Void, self.parse(cursor, interner)?).into()) } TokenKind::Keyword((Keyword::TypeOf, false)) => { cursor.next(interner)?.expect("TypeOf keyword vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::TypeOf, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::TypeOf, self.parse(cursor, interner)?).into()) } TokenKind::Punctuator(Punctuator::Add) => { cursor.next(interner)?.expect("+ token vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::Plus, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::Plus, self.parse(cursor, interner)?).into()) } TokenKind::Punctuator(Punctuator::Sub) => { cursor.next(interner)?.expect("- token vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::Minus, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::Minus, self.parse(cursor, interner)?).into()) } TokenKind::Punctuator(Punctuator::Neg) => { cursor.next(interner)?.expect("~ token vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::Tilde, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::Tilde, self.parse(cursor, interner)?).into()) } TokenKind::Punctuator(Punctuator::Not) => { cursor.next(interner)?.expect("! token vanished"); // Consume the token. - Ok(node::UnaryOp::new(UnaryOp::Not, self.parse(cursor, interner)?).into()) + Ok(Unary::new(UnaryOp::Not, self.parse(cursor, interner)?).into()) } TokenKind::Keyword((Keyword::Await, true)) if self.allow_await.0 => { Err(ParseError::general( diff --git a/boa_engine/src/syntax/parser/expression/update.rs b/boa_engine/src/syntax/parser/expression/update.rs index c3364a8b590..2e433ccd697 100644 --- a/boa_engine/src/syntax/parser/expression/update.rs +++ b/boa_engine/src/syntax/parser/expression/update.rs @@ -7,7 +7,10 @@ use super::left_hand_side::LeftHandSideExpression; use crate::syntax::{ - ast::{node, op::UnaryOp, Node, Punctuator}, + ast::{ + expression::operator::{unary::op::UnaryOp, Unary}, + Expression, Punctuator, + }, lexer::{Error as LexError, TokenKind}, parser::{ expression::unary::UnaryExpression, AllowAwait, AllowYield, Cursor, ParseError, @@ -51,9 +54,9 @@ impl TokenParser for UpdateExpression where R: Read, { - type Output = Node; + type Output = Expression; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("UpdateExpression", "Parsing"); let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -68,12 +71,12 @@ where .parse(cursor, interner)?; if cursor.strict_mode() { - if let Node::Identifier(ident) = target { + if let Expression::Identifier(ident) = target { ident.check_strict_arguments_or_eval(position)?; } } - return Ok(node::UnaryOp::new(UnaryOp::IncrementPre, target).into()); + return Ok(Unary::new(UnaryOp::IncrementPre, target).into()); } TokenKind::Punctuator(Punctuator::Dec) => { cursor @@ -84,12 +87,12 @@ where .parse(cursor, interner)?; if cursor.strict_mode() { - if let Node::Identifier(ident) = target { + if let Expression::Identifier(ident) = target { ident.check_strict_arguments_or_eval(position)?; } } - return Ok(node::UnaryOp::new(UnaryOp::DecrementPre, target).into()); + return Ok(Unary::new(UnaryOp::DecrementPre, target).into()); } _ => {} } @@ -107,13 +110,13 @@ where .expect("Punctuator::Inc token disappeared"); // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors let ok = match &lhs { - Node::Identifier(_) if !strict => true, - Node::Identifier(ident) + Expression::Identifier(_) if !strict => true, + Expression::Identifier(ident) if ![Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) => { true } - Node::GetConstField(_) | Node::GetField(_) => true, + Expression::PropertyAccess(_) => true, _ => false, }; if !ok { @@ -123,7 +126,7 @@ where ))); } - return Ok(node::UnaryOp::new(UnaryOp::IncrementPost, lhs).into()); + return Ok(Unary::new(UnaryOp::IncrementPost, lhs).into()); } TokenKind::Punctuator(Punctuator::Dec) => { cursor @@ -131,13 +134,13 @@ where .expect("Punctuator::Dec token disappeared"); // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors let ok = match &lhs { - Node::Identifier(_) if !strict => true, - Node::Identifier(ident) + Expression::Identifier(_) if !strict => true, + Expression::Identifier(ident) if ![Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) => { true } - Node::GetConstField(_) | Node::GetField(_) => true, + Expression::PropertyAccess(_) => true, _ => false, }; if !ok { @@ -147,7 +150,7 @@ where ))); } - return Ok(node::UnaryOp::new(UnaryOp::DecrementPost, lhs).into()); + return Ok(Unary::new(UnaryOp::DecrementPost, lhs).into()); } _ => {} } diff --git a/boa_engine/src/syntax/parser/function/mod.rs b/boa_engine/src/syntax/parser/function/mod.rs index aebec3ecb74..3b420589b89 100644 --- a/boa_engine/src/syntax/parser/function/mod.rs +++ b/boa_engine/src/syntax/parser/function/mod.rs @@ -10,27 +10,27 @@ #[cfg(test)] mod tests; -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{self, FormalParameterList}, - node::{declaration::Declaration, FormalParameterListFlags}, - Punctuator, - }, - lexer::{Error as LexError, InputElement, TokenKind}, - parser::{ - expression::{BindingIdentifier, Initializer}, - statement::{ArrayBindingPattern, ObjectBindingPattern, StatementList}, - AllowAwait, AllowYield, Cursor, ParseError, TokenParser, - }, +use crate::syntax::{ + ast::{ + function::{self, FormalParameterList, FormalParameterListFlags}, + statement::{self, declaration::Declaration}, + Punctuator, + }, + lexer::{Error as LexError, InputElement, TokenKind}, + parser::{ + expression::{BindingIdentifier, Initializer}, + statement::{ArrayBindingPattern, ObjectBindingPattern, StatementList}, + AllowAwait, AllowYield, Cursor, ParseError, TokenParser, }, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; use boa_profiler::Profiler; use rustc_hash::FxHashSet; use std::io::Read; +use super::ParseResult; + /// Formal parameters parsing. /// /// More information: @@ -65,11 +65,7 @@ where { type Output = FormalParameterList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FormalParameters", "Parsing"); cursor.set_goal(InputElement::RegExp); @@ -219,11 +215,7 @@ where { type Output = FormalParameterList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let params_start_position = cursor .expect( TokenKind::Punctuator(Punctuator::OpenParen), @@ -293,13 +285,9 @@ impl TokenParser for BindingRestElement where R: Read, { - type Output = node::FormalParameter; + type Output = function::FormalParameter; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("BindingRestElement", "Parsing"); cursor.expect(Punctuator::Spread, "rest parameter", interner)?; @@ -321,16 +309,15 @@ where .parse(cursor, interner) }) .transpose()?; - Declaration::new_with_object_pattern(param, init) + Declaration::from_pattern(param.into(), init) } - TokenKind::Punctuator(Punctuator::OpenBracket) => { - Declaration::new_with_array_pattern( - ArrayBindingPattern::new(self.allow_yield, self.allow_await) - .parse(cursor, interner)?, - None, - ) - } + TokenKind::Punctuator(Punctuator::OpenBracket) => Declaration::from_pattern( + ArrayBindingPattern::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)? + .into(), + None, + ), _ => { let params = BindingIdentifier::new(self.allow_yield, self.allow_await) @@ -348,13 +335,13 @@ where }) .transpose()?; - Declaration::new_with_identifier(params, init) + Declaration::from_identifier(params.into(), init) } }; Ok(Self::Output::new(declaration, true)) } else { Ok(Self::Output::new( - Declaration::new_with_identifier(Sym::EMPTY_STRING, None), + Declaration::from_identifier(Sym::EMPTY_STRING.into(), None), true, )) } @@ -393,13 +380,9 @@ impl TokenParser for FormalParameter where R: Read, { - type Output = node::FormalParameter; + type Output = function::FormalParameter; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FormalParameter", "Parsing"); if let Some(t) = cursor.peek(0, interner)? { @@ -421,7 +404,7 @@ where None }; - Declaration::new_with_object_pattern(bindings, init) + Declaration::from_pattern(bindings.into(), init) } TokenKind::Punctuator(Punctuator::OpenBracket) => { let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await) @@ -440,7 +423,7 @@ where None }; - Declaration::new_with_array_pattern(bindings, init) + Declaration::from_pattern(bindings.into(), init) } _ => { let ident = BindingIdentifier::new(self.allow_yield, self.allow_await) @@ -459,13 +442,13 @@ where None }; - Declaration::new_with_identifier(ident, init) + Declaration::from_identifier(ident.into(), init) } }; Ok(Self::Output::new(declaration, false)) } else { Ok(Self::Output::new( - Declaration::new_with_identifier(Sym::EMPTY_STRING, None), + Declaration::from_identifier(Sym::EMPTY_STRING.into(), None), false, )) } @@ -514,13 +497,9 @@ impl TokenParser for FunctionStatementList where R: Read, { - type Output = node::StatementList; + type Output = statement::StatementList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FunctionStatementList", "Parsing"); let global_strict_mode = cursor.strict_mode(); diff --git a/boa_engine/src/syntax/parser/function/tests.rs b/boa_engine/src/syntax/parser/function/tests.rs index 29dad43b181..2fe643e31aa 100644 --- a/boa_engine/src/syntax/parser/function/tests.rs +++ b/boa_engine/src/syntax/parser/function/tests.rs @@ -1,12 +1,23 @@ use crate::{ string::utf16, syntax::{ - ast::node::{ - ArrowFunctionDecl, BinOp, Declaration, DeclarationList, FormalParameter, - FormalParameterList, FunctionDecl, Identifier, Node, Return, + ast::{ + expression::{ + operator::{binary::op::ArithmeticOp, Binary}, + Identifier, + }, + function::{ + ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, + Function, + }, + statement::{ + declaration::{Declaration, DeclarationList}, + Return, StatementList, + }, + Expression, }, - ast::{node::FormalParameterListFlags, op::NumOp}, - parser::{tests::check_parser, Parser}, + parser::tests::check_parser, + Parser, }, Context, }; @@ -18,12 +29,12 @@ fn check_basic() { let mut interner = Interner::default(); check_parser( "function foo(a) { return a; }", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -32,10 +43,11 @@ fn check_basic() { length: 1, }, vec![Return::new( - Identifier::from(interner.get_or_intern_static("a", utf16!("a"))), + Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), None, ) - .into()], + .into()] + .into(), ) .into()], interner, @@ -48,20 +60,20 @@ fn check_duplicates_strict_off() { let mut interner = Interner::default(); check_parser( "function foo(a, a) { return a; }", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -72,10 +84,11 @@ fn check_duplicates_strict_off() { length: 2, }, vec![Return::new( - Identifier::from(interner.get_or_intern_static("a", utf16!("a"))), + Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), None, ) - .into()], + .into()] + .into(), ) .into()], interner, @@ -98,12 +111,12 @@ fn check_basic_semicolon_insertion() { let mut interner = Interner::default(); check_parser( "function foo(a) { return a }", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -112,10 +125,11 @@ fn check_basic_semicolon_insertion() { length: 1, }, vec![Return::new( - Identifier::from(interner.get_or_intern_static("a", utf16!("a"))), + Some(Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into()), None, ) - .into()], + .into()] + .into(), ) .into()], interner, @@ -128,12 +142,12 @@ fn check_empty_return() { let mut interner = Interner::default(); check_parser( "function foo(a) { return; }", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -141,7 +155,7 @@ fn check_empty_return() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>(None, None).into()], + vec![Return::new(None, None).into()].into(), ) .into()], interner, @@ -154,12 +168,12 @@ fn check_empty_return_semicolon_insertion() { let mut interner = Interner::default(); check_parser( "function foo(a) { return }", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -167,7 +181,7 @@ fn check_empty_return_semicolon_insertion() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>(None, None).into()], + vec![Return::new(None, None).into()].into(), ) .into()], interner, @@ -180,20 +194,20 @@ fn check_rest_operator() { let mut interner = Interner::default(); check_parser( "function foo(a, ...b) {}", - vec![FunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Function::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), true, @@ -203,7 +217,7 @@ fn check_rest_operator() { .union(FormalParameterListFlags::HAS_REST_PARAMETER), length: 1, }, - vec![], + StatementList::default(), ) .into()], interner, @@ -216,12 +230,12 @@ fn check_arrow_only_rest() { let mut interner = Interner::default(); check_parser( "(...a) => {}", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), true, @@ -230,8 +244,8 @@ fn check_arrow_only_rest() { .union(FormalParameterListFlags::HAS_REST_PARAMETER), length: 0, }, - vec![], - ) + StatementList::default(), + )) .into()], interner, ); @@ -243,27 +257,27 @@ fn check_arrow_rest() { let mut interner = Interner::default(); check_parser( "(a, b, ...c) => {}", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), None, ), true, @@ -273,8 +287,8 @@ fn check_arrow_rest() { .union(FormalParameterListFlags::HAS_REST_PARAMETER), length: 2, }, - vec![], - ) + StatementList::default(), + )) .into()], interner, ); @@ -286,20 +300,20 @@ fn check_arrow() { let mut interner = Interner::default(); check_parser( "(a, b) => { return a + b; }", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -309,15 +323,19 @@ fn check_arrow() { length: 2, }, vec![Return::new( - BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), + Some( + Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), ), None, ) - .into()], - ) + .into()] + .into(), + )) .into()], interner, ); @@ -329,20 +347,20 @@ fn check_arrow_semicolon_insertion() { let mut interner = Interner::default(); check_parser( "(a, b) => { return a + b }", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -352,15 +370,19 @@ fn check_arrow_semicolon_insertion() { length: 2, }, vec![Return::new( - BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), + Some( + Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), ), None, ) - .into()], - ) + .into()] + .into(), + )) .into()], interner, ); @@ -372,20 +394,20 @@ fn check_arrow_epty_return() { let mut interner = Interner::default(); check_parser( "(a, b) => { return; }", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -394,8 +416,8 @@ fn check_arrow_epty_return() { flags: FormalParameterListFlags::default(), length: 2, }, - vec![Return::new::, Option<_>>(None, None).into()], - ) + vec![Return::new(None, None).into()].into(), + )) .into()], interner, ); @@ -407,20 +429,20 @@ fn check_arrow_empty_return_semicolon_insertion() { let mut interner = Interner::default(); check_parser( "(a, b) => { return }", - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -429,8 +451,8 @@ fn check_arrow_empty_return_semicolon_insertion() { flags: FormalParameterListFlags::default(), length: 2, }, - vec![Return::new::, Option<_>>(None, None).into()], - ) + vec![Return::new(None, None).into()].into(), + )) .into()], interner, ); @@ -442,15 +464,15 @@ fn check_arrow_assignment() { check_parser( "let foo = (a) => { return a };", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -458,14 +480,15 @@ fn check_arrow_assignment() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -483,15 +506,15 @@ fn check_arrow_assignment_nobrackets() { check_parser( "let foo = (a) => a;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Declaration::from_identifier( + interner.get_or_intern_static("foo", utf16!("foo")).into(), Some( - ArrowFunctionDecl::new( - interner.get_or_intern_static("foo", utf16!("foo")), + ArrowFunction::new( + interner.get_or_intern_static("foo", utf16!("foo")).into(), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -499,14 +522,15 @@ fn check_arrow_assignment_nobrackets() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -524,15 +548,15 @@ fn check_arrow_assignment_noparenthesis() { check_parser( "let foo = a => { return a };", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("foo", utf16!("foo")), + vec![Declaration::from_identifier( + interner.get_or_intern_static("foo", utf16!("foo")).into(), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -540,14 +564,15 @@ fn check_arrow_assignment_noparenthesis() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -565,15 +590,15 @@ fn check_arrow_assignment_noparenthesis_nobrackets() { check_parser( "let foo = a => a;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, @@ -581,14 +606,15 @@ fn check_arrow_assignment_noparenthesis_nobrackets() { flags: FormalParameterListFlags::default(), length: 1, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -606,23 +632,23 @@ fn check_arrow_assignment_2arg() { check_parser( "let foo = (a, b) => { return a };", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -631,14 +657,15 @@ fn check_arrow_assignment_2arg() { flags: FormalParameterListFlags::default(), length: 2, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -656,23 +683,23 @@ fn check_arrow_assignment_2arg_nobrackets() { check_parser( "let foo = (a, b) => a;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, @@ -681,14 +708,15 @@ fn check_arrow_assignment_2arg_nobrackets() { flags: FormalParameterListFlags::default(), length: 2, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -706,30 +734,30 @@ fn check_arrow_assignment_3arg() { check_parser( "let foo = (a, b, c) => { return a };", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), None, ), false, @@ -738,14 +766,15 @@ fn check_arrow_assignment_3arg() { flags: FormalParameterListFlags::default(), length: 3, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), @@ -763,30 +792,30 @@ fn check_arrow_assignment_3arg_nobrackets() { check_parser( "let foo = (a, b, c) => a;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( + vec![Declaration::from_identifier( Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( - ArrowFunctionDecl::new( + ArrowFunction::new( Some(interner.get_or_intern_static("foo", utf16!("foo"))), FormalParameterList { parameters: Box::new([ FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), false, ), FormalParameter::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), None, ), false, @@ -795,14 +824,15 @@ fn check_arrow_assignment_3arg_nobrackets() { flags: FormalParameterListFlags::default(), length: 3, }, - vec![Return::new::, Option<_>>( + vec![Return::new( Some( Identifier::new(interner.get_or_intern_static("a", utf16!("a"))) .into(), ), None, ) - .into()], + .into()] + .into(), ) .into(), ), diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs index 64ae7d0f7c1..9d2313f15c9 100644 --- a/boa_engine/src/syntax/parser/mod.rs +++ b/boa_engine/src/syntax/parser/mod.rs @@ -14,10 +14,6 @@ mod tests; use crate::{ string::utf16, syntax::{ - ast::{ - node::{ContainsSymbol, FormalParameterList, StatementList}, - Position, - }, lexer::TokenKind, parser::{ cursor::Cursor, @@ -33,6 +29,10 @@ use std::io::Read; pub use self::error::{ParseError, ParseResult}; pub(in crate::syntax) use expression::RESERVED_IDENTIFIERS_STRICT; +use super::ast::{ + function::FormalParameterList, statement::StatementList, ContainsSymbol, Position, +}; + /// Trait implemented by parsers. /// /// This makes it possible to abstract over the underlying implementation of a parser. @@ -46,11 +46,7 @@ where /// Parses the token stream using the current parser. /// /// This method needs to be provided by the implementor type. - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result; + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult; } /// Boolean representing if the parser should allow a `yield` keyword. @@ -174,7 +170,7 @@ impl Parser { let mut contains_super_call = false; let mut contains_new_target = false; if direct { - for node in statement_list.items() { + for node in statement_list.statements() { if !contains_super_property && node.contains(ContainsSymbol::SuperProperty) { contains_super_property = true; } @@ -287,7 +283,7 @@ impl Script { // It is a Syntax Error if the LexicallyDeclaredNames of ScriptBody contains any duplicate entries. // It is a Syntax Error if any element of the LexicallyDeclaredNames of ScriptBody also occurs in the VarDeclaredNames of ScriptBody. let mut var_declared_names = FxHashSet::default(); - statement_list.var_declared_names_new(&mut var_declared_names); + statement_list.var_declared_names(&mut var_declared_names); let lexically_declared_names = statement_list.lexically_declared_names(); let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); for (name, is_function_declaration) in &lexically_declared_names { @@ -374,16 +370,12 @@ where { type Output = StatementList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let body = self::statement::StatementList::new(false, false, false, &[]) .parse(cursor, interner)?; if !self.direct_eval { - for node in body.items() { + for node in body.statements() { // It is a Syntax Error if StatementList Contains super unless the source text containing super is eval // code that is being processed by a direct eval. // Additional early error rules for super within direct eval are defined in 19.2.1.1. diff --git a/boa_engine/src/syntax/parser/statement/block/mod.rs b/boa_engine/src/syntax/parser/statement/block/mod.rs index 4ff1aa4ed50..5da1d6350c9 100644 --- a/boa_engine/src/syntax/parser/statement/block/mod.rs +++ b/boa_engine/src/syntax/parser/statement/block/mod.rs @@ -12,9 +12,9 @@ mod tests; use super::StatementList; use crate::syntax::{ - ast::{node, Punctuator}, + ast::{statement, Punctuator}, lexer::TokenKind, - parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, + parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -67,19 +67,15 @@ impl TokenParser for Block where R: Read, { - type Output = node::Block; + type Output = statement::Block; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Block", "Parsing"); cursor.expect(Punctuator::OpenBlock, "block", interner)?; if let Some(tk) = cursor.peek(0, interner)? { if tk.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock) { cursor.next(interner)?.expect("} token vanished"); - return Ok(node::Block::from(vec![])); + return Ok(statement::Block::from(vec![])); } } let position = cursor @@ -94,7 +90,7 @@ where &BLOCK_BREAK_TOKENS, ) .parse(cursor, interner) - .map(node::Block::from)?; + .map(statement::Block::from)?; cursor.expect(Punctuator::CloseBlock, "block", interner)?; let lexically_declared_names = statement_list.lexically_declared_names(); @@ -115,7 +111,7 @@ where } let mut var_declared_names = FxHashSet::default(); - for node in statement_list.items() { + for node in statement_list.statements() { node.var_declared_names(&mut var_declared_names); } for (lex_name, _) in &lexically_declared_names { diff --git a/boa_engine/src/syntax/parser/statement/block/tests.rs b/boa_engine/src/syntax/parser/statement/block/tests.rs index c5167218be8..bf06a72bcc9 100644 --- a/boa_engine/src/syntax/parser/statement/block/tests.rs +++ b/boa_engine/src/syntax/parser/statement/block/tests.rs @@ -1,25 +1,29 @@ //! Block statement parsing tests. -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - Assign, Block, Call, Declaration, DeclarationList, FormalParameterList, - FunctionDecl, Identifier, Node, Return, UnaryOp, - }, - op, Const, +use crate::syntax::{ + ast::{ + expression::{ + literal::Literal, + operator::{assign::op::AssignOp, unary::op::UnaryOp, Assign, Unary}, + Call, Identifier, }, - parser::tests::check_parser, + function::{FormalParameterList, Function}, + statement::{ + declaration::{Declaration, DeclarationList}, + Block, Return, + }, + Expression, Statement, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; /// Helper function to check a block. #[track_caller] fn check_block(js: &str, block: B, interner: Interner) where - B: Into>, + B: Into>, { check_parser(js, vec![Block::from(block.into()).into()], interner); } @@ -40,14 +44,18 @@ fn non_empty() { }", vec![ DeclarationList::Var( - vec![Declaration::new_with_identifier( - a, - Some(Const::from(10).into()), + vec![Declaration::from_identifier( + a.into(), + Some(Literal::from(10).into()), )] .into(), ) .into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), ], interner, ); @@ -65,21 +73,25 @@ fn non_empty() { a++; }", vec![ - FunctionDecl::new( - hello, + Function::new( + hello.into(), FormalParameterList::default(), - vec![Return::new(Const::from(10), None).into()], + vec![Return::new(Some(Literal::from(10).into()), None).into()].into(), ) .into(), DeclarationList::Var( - vec![Declaration::new_with_identifier( - a, - Node::from(Call::new(Identifier::new(hello), vec![])), + vec![Declaration::from_identifier( + a.into(), + Some(Call::new(Identifier::new(hello).into(), Box::default()).into()), )] .into(), ) .into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), ], interner, ); @@ -98,21 +110,25 @@ fn hoisting() { function hello() { return 10 } }", vec![ - FunctionDecl::new( - hello, + Function::new( + Some(hello), FormalParameterList::default(), - vec![Return::new(Const::from(10), None).into()], + vec![Return::new(Some(Literal::from(10).into()), None).into()].into(), ) .into(), DeclarationList::Var( - vec![Declaration::new_with_identifier( - a, - Node::from(Call::new(Identifier::new(hello), vec![])), + vec![Declaration::from_identifier( + a.into(), + Some(Call::new(Identifier::new(hello).into(), Box::default()).into()), )] .into(), ) .into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), ], interner, ); @@ -127,9 +143,18 @@ fn hoisting() { var a; }", vec![ - Assign::new(Identifier::new(a), Const::from(10)).into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), - DeclarationList::Var(vec![Declaration::new_with_identifier(a, None)].into()).into(), + Expression::from(Assign::new( + AssignOp::Assign, + Identifier::new(a).into(), + Literal::from(10).into(), + )) + .into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), + DeclarationList::Var(vec![Declaration::from_identifier(a.into(), None)].into()).into(), ], interner, ); 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 1190846fb42..efc273759fb 100644 --- a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs @@ -11,12 +11,12 @@ mod tests; use crate::syntax::{ - ast::{node::Break, Keyword, Punctuator}, + ast::{statement::Break, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, expression::LabelIdentifier, - AllowAwait, AllowYield, ParseError, TokenParser, + AllowAwait, AllowYield, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -57,11 +57,7 @@ where { type Output = Break; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("BreakStatement", "Parsing"); cursor.expect((Keyword::Break, false), "break statement", interner)?; 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 764293a4587..dac6628bb9a 100644 --- a/boa_engine/src/syntax/parser/statement/break_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/break_stm/tests.rs @@ -1,20 +1,18 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{Block, Break, Node, WhileLoop}, - Const, - }, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + statement::{Block, Break, WhileLoop}, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn inline() { check_parser( "while (true) break;", - vec![WhileLoop::new(Const::from(true), Node::Break(Break::new(None))).into()], + vec![WhileLoop::new(Literal::from(true).into(), Break::new(None).into()).into()], Interner::default(), ); } @@ -24,7 +22,7 @@ fn new_line() { check_parser( "while (true) break;", - vec![WhileLoop::new(Const::from(true), Break::new(None)).into()], + vec![WhileLoop::new(Literal::from(true).into(), Break::new(None).into()).into()], Interner::default(), ); } @@ -34,8 +32,8 @@ fn inline_block_semicolon_insertion() { check_parser( "while (true) {break}", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Break::new(None).into()]).into(), ) .into()], Interner::default(), @@ -50,11 +48,12 @@ fn new_line_semicolon_insertion() { break test }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new( + Literal::from(true).into(), + Block::from(vec![Break::new(Some( interner.get_or_intern_static("test", utf16!("test")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -66,8 +65,8 @@ fn inline_block() { check_parser( "while (true) {break;}", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Break::new(None).into()]).into(), ) .into()], Interner::default(), @@ -82,11 +81,12 @@ fn new_line_block() { break test; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new( + Literal::from(true).into(), + Block::from(vec![Break::new(Some( interner.get_or_intern_static("test", utf16!("test")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -101,11 +101,12 @@ fn reserved_label() { break await; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new( + Literal::from(true).into(), + Block::from(vec![Break::new(Some( interner.get_or_intern_static("await", utf16!("await")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -117,11 +118,12 @@ fn reserved_label() { break yield; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new( + Literal::from(true).into(), + Block::from(vec![Break::new(Some( interner.get_or_intern_static("yield", utf16!("yield")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -135,8 +137,8 @@ fn new_line_block_empty() { break; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Break::new(None).into()]).into(), ) .into()], Interner::default(), @@ -150,8 +152,8 @@ fn new_line_block_empty_semicolon_insertion() { break }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Break::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Break::new(None).into()]).into(), ) .into()], Interner::default(), 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 49e94797653..65d33b44f52 100644 --- a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs @@ -11,12 +11,12 @@ mod tests; use crate::syntax::{ - ast::{node::Continue, Keyword, Punctuator}, + ast::{statement::Continue, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, expression::LabelIdentifier, - AllowAwait, AllowYield, ParseError, TokenParser, + AllowAwait, AllowYield, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -57,11 +57,7 @@ where { type Output = Continue; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ContinueStatement", "Parsing"); cursor.expect((Keyword::Continue, false), "continue statement", interner)?; 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 f8a14c99340..fba06f972c2 100644 --- a/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs @@ -1,20 +1,18 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{Block, Continue, WhileLoop}, - Const, - }, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + statement::{Block, Continue, WhileLoop}, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn inline() { check_parser( "while (true) continue;", - vec![WhileLoop::new(Const::from(true), Continue::new(None)).into()], + vec![WhileLoop::new(Literal::from(true).into(), Continue::new(None).into()).into()], Interner::default(), ); } @@ -24,7 +22,7 @@ fn new_line() { check_parser( "while (true) continue;", - vec![WhileLoop::new(Const::from(true), Continue::new(None)).into()], + vec![WhileLoop::new(Literal::from(true).into(), Continue::new(None).into()).into()], Interner::default(), ); } @@ -34,8 +32,8 @@ fn inline_block_semicolon_insertion() { check_parser( "while (true) {continue}", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Continue::new(None).into()]).into(), ) .into()], Interner::default(), @@ -50,11 +48,12 @@ fn new_line_semicolon_insertion() { continue test }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new( + Literal::from(true).into(), + Block::from(vec![Continue::new(Some( interner.get_or_intern_static("test", utf16!("test")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -66,8 +65,8 @@ fn inline_block() { check_parser( "while (true) {continue;}", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Continue::new(None).into()]).into(), ) .into()], Interner::default(), @@ -82,11 +81,12 @@ fn new_line_block() { continue test; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new( + Literal::from(true).into(), + Block::from(vec![Continue::new(Some( interner.get_or_intern_static("test", utf16!("test")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -101,11 +101,12 @@ fn reserved_label() { continue await; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new( + Literal::from(true).into(), + Block::from(vec![Continue::new(Some( interner.get_or_intern_static("await", utf16!("await")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -117,11 +118,12 @@ fn reserved_label() { continue yield; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new( + Literal::from(true).into(), + Block::from(vec![Continue::new(Some( interner.get_or_intern_static("yield", utf16!("yield")), - ) - .into()]), + )) + .into()]) + .into(), ) .into()], interner, @@ -135,8 +137,8 @@ fn new_line_block_empty() { continue; }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Continue::new(None).into()]).into(), ) .into()], Interner::default(), @@ -150,8 +152,8 @@ fn new_line_block_empty_semicolon_insertion() { continue }", vec![WhileLoop::new( - Const::from(true), - Block::from(vec![Continue::new(None).into()]), + Literal::from(true).into(), + Block::from(vec![Continue::new(None).into()]).into(), ) .into()], Interner::default(), 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 951b53b653c..9286aa0229c 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 @@ -2,10 +2,11 @@ mod tests; use crate::syntax::{ - ast::{node::AsyncFunctionDecl, Keyword}, + ast::function::AsyncFunction, + ast::Keyword, parser::{ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -73,13 +74,9 @@ impl TokenParser for AsyncFunctionDeclaration where R: Read, { - type Output = AsyncFunctionDecl; + type Output = AsyncFunction; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect( (Keyword::Async, false), "async function declaration", @@ -94,6 +91,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(AsyncFunctionDecl::new(result.0, result.1, result.2)) + Ok(AsyncFunction::new(Some(result.0), result.1, result.2)) } } 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 643f1c32d68..b9577b883ac 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,11 +1,12 @@ -use crate::{ - string::utf16, - syntax::{ - ast::node::{AsyncFunctionDecl, FormalParameterList}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + function::{AsyncFunction, FormalParameterList}, + statement::StatementList, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; /// Async function declaration parsing. #[test] @@ -13,10 +14,10 @@ fn async_function_declaration() { let mut interner = Interner::default(); check_parser( "async function hello() {}", - vec![AsyncFunctionDecl::new( - interner.get_or_intern_static("hello", utf16!("hello")), + vec![AsyncFunction::new( + Some(interner.get_or_intern_static("hello", utf16!("hello"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, @@ -29,10 +30,10 @@ fn async_function_declaration_keywords() { let mut interner = Interner::default(); check_parser( "async function yield() {}", - vec![AsyncFunctionDecl::new( - interner.get_or_intern_static("yield", utf16!("yield")), + vec![AsyncFunction::new( + Some(interner.get_or_intern_static("yield", utf16!("yield"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, @@ -41,10 +42,10 @@ fn async_function_declaration_keywords() { let mut interner = Interner::default(); check_parser( "async function await() {}", - vec![AsyncFunctionDecl::new( - interner.get_or_intern_static("await", utf16!("await")), + vec![AsyncFunction::new( + Some(interner.get_or_intern_static("await", utf16!("await"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, 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 56f6cacf1ad..5dd8ef292da 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 @@ -7,10 +7,10 @@ mod tests; use crate::syntax::{ - ast::{node::AsyncGeneratorDecl, Keyword, Punctuator}, + ast::{function::AsyncGenerator, Keyword, Punctuator}, parser::{ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -91,13 +91,9 @@ impl TokenParser for AsyncGeneratorDeclaration where R: Read, { - type Output = AsyncGeneratorDecl; + type Output = AsyncGenerator; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect( (Keyword::Async, false), "async generator declaration", @@ -113,6 +109,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(AsyncGeneratorDecl::new(result.0, result.1, result.2)) + Ok(AsyncGenerator::new(Some(result.0), result.1, result.2)) } } 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 ef80f70398c..3fc188f7f59 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,21 +1,22 @@ -use crate::{ - string::utf16, - syntax::{ - ast::node::{AsyncGeneratorDecl, FormalParameterList}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + function::{AsyncGenerator, FormalParameterList}, + statement::StatementList, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn async_generator_function_declaration() { let mut interner = Interner::default(); check_parser( "async function* gen() {}", - vec![AsyncGeneratorDecl::new( - interner.get_or_intern_static("gen", utf16!("gen")), + vec![AsyncGenerator::new( + Some(interner.get_or_intern_static("gen", utf16!("gen"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, 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 0d568958c73..c12ce4e3a22 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 @@ -3,14 +3,12 @@ mod tests; use crate::syntax::{ ast::{ - node::{ - self, - declaration::class_decl::ClassElement as ClassElementNode, - function_contains_super, has_direct_super, - object::{ClassElementName, MethodDefinition, PropertyName::Literal}, - Class, ContainsSymbol, FormalParameterList, FunctionExpr, + self, + function::{ + self, function_contains_super, has_direct_super, Class, FormalParameterList, Function, }, - Keyword, Punctuator, + property::{ClassElementName, MethodDefinition}, + ContainsSymbol, Expression, Keyword, Punctuator, Statement, }, lexer::{Error as LexError, TokenKind}, parser::{ @@ -20,11 +18,10 @@ use crate::syntax::{ }, function::{FormalParameters, FunctionBody, UniqueFormalParameters, FUNCTION_BREAK_TOKENS}, statement::StatementList, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; -use node::Node; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -63,13 +60,9 @@ impl TokenParser for ClassDeclaration where R: Read, { - type Output = Node; + type Output = Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect((Keyword::Class, false), "class declaration", interner)?; let strict = cursor.strict_mode(); cursor.set_strict_mode(true); @@ -91,7 +84,7 @@ where }; cursor.set_strict_mode(strict); - Ok(Node::ClassDecl( + Ok(Statement::Class( ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?, )) } @@ -131,11 +124,7 @@ where { type Output = Class; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let super_ref = match token.kind() { TokenKind::Keyword((Keyword::Extends, true)) => { @@ -144,9 +133,9 @@ where token.span().start(), )); } - TokenKind::Keyword((Keyword::Extends, false)) => Some(Box::new( + TokenKind::Keyword((Keyword::Extends, false)) => Some( ClassHeritage::new(self.allow_yield, self.allow_await).parse(cursor, interner)?, - )), + ), _ => None, }; @@ -164,7 +153,7 @@ where if is_close_block { cursor.next(interner).expect("token disappeared"); - Ok(Class::new(self.name, super_ref, None, vec![])) + Ok(Class::new(Some(self.name), super_ref, None, Box::default())) } else { let body_start = cursor .peek(0, interner)? @@ -187,7 +176,12 @@ where } } - Ok(Class::new(self.name, super_ref, constructor, elements)) + Ok(Class::new( + Some(self.name), + super_ref, + constructor, + elements.into(), + )) } } } @@ -222,13 +216,9 @@ impl TokenParser for ClassHeritage where R: Read, { - type Output = Node; + type Output = Expression; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect( TokenKind::Keyword((Keyword::Extends, false)), "class heritage", @@ -277,13 +267,9 @@ impl TokenParser for ClassBody where R: Read, { - type Output = (Option, Vec); + type Output = (Option, Vec); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.push_private_environment(); let mut constructor = None; @@ -313,7 +299,7 @@ where } (None, Some(element)) => { match &element { - ClassElementNode::PrivateMethodDefinition(name, method) => { + function::ClassElement::PrivateMethodDefinition(name, method) => { // It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. if has_direct_super(method.body(), method.parameters()) { return Err(ParseError::lex(LexError::Syntax( @@ -371,7 +357,7 @@ where } } } - ClassElementNode::PrivateStaticMethodDefinition(name, method) => { + function::ClassElement::PrivateStaticMethodDefinition(name, method) => { // It is a Syntax Error if HasDirectSuper of MethodDefinition is true. if has_direct_super(method.body(), method.parameters()) { return Err(ParseError::lex(LexError::Syntax( @@ -429,9 +415,9 @@ where } } } - ClassElementNode::PrivateFieldDefinition(name, init) => { + function::ClassElement::PrivateFieldDefinition(name, init) => { if let Some(node) = init { - if node.contains(node::ContainsSymbol::SuperCall) { + if node.contains(ContainsSymbol::SuperCall) { return Err(ParseError::lex(LexError::Syntax( "invalid super usage".into(), position, @@ -448,9 +434,9 @@ where )); } } - ClassElementNode::PrivateStaticFieldDefinition(name, init) => { + function::ClassElement::PrivateStaticFieldDefinition(name, init) => { if let Some(node) = init { - if node.contains(node::ContainsSymbol::SuperCall) { + if node.contains(ContainsSymbol::SuperCall) { return Err(ParseError::lex(LexError::Syntax( "invalid super usage".into(), position, @@ -467,8 +453,8 @@ where )); } } - ClassElementNode::MethodDefinition(_, method) - | ClassElementNode::StaticMethodDefinition(_, method) => { + function::ClassElement::MethodDefinition(_, method) + | function::ClassElement::StaticMethodDefinition(_, method) => { // ClassElement : MethodDefinition: // It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. // ClassElement : static MethodDefinition: @@ -480,9 +466,9 @@ where ))); } } - ClassElementNode::FieldDefinition(_, Some(node)) - | ClassElementNode::StaticFieldDefinition(_, Some(node)) => { - if node.contains(node::ContainsSymbol::SuperCall) { + function::ClassElement::FieldDefinition(_, Some(node)) + | function::ClassElement::StaticFieldDefinition(_, Some(node)) => { + if node.contains(ContainsSymbol::SuperCall) { return Err(ParseError::lex(LexError::Syntax( "invalid super usage".into(), position, @@ -548,13 +534,9 @@ impl TokenParser for ClassElement where R: Read, { - type Output = (Option, Option); + type Output = (Option, Option); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let r#static = match token.kind() { TokenKind::Punctuator(Punctuator::Semicolon) => { @@ -622,7 +604,7 @@ where )?; cursor.set_strict_mode(strict); - return Ok((Some(FunctionExpr::new(self.name, parameters, body)), None)); + return Ok((Some(Function::new(Some(self.name), parameters, body)), None)); } TokenKind::Punctuator(Punctuator::OpenBlock) if r#static => { cursor.next(interner).expect("token disappeared"); @@ -630,7 +612,7 @@ where .next_if(TokenKind::Punctuator(Punctuator::CloseBlock), interner)? .is_some() { - node::StatementList::from(vec![]) + ast::statement::StatementList::from(vec![]) } else { let strict = cursor.strict_mode(); cursor.set_strict_mode(true); @@ -664,7 +646,7 @@ where } let mut var_declared_names = FxHashSet::default(); - statement_list.var_declared_names_new(&mut var_declared_names); + statement_list.var_declared_names(&mut var_declared_names); for (lex_name, _) in &lexically_declared_names { if var_declared_names.contains(lex_name) { return Err(ParseError::general( @@ -682,7 +664,7 @@ where cursor.set_strict_mode(strict); statement_list }; - ClassElementNode::StaticBlock(statement_list) + function::ClassElement::StaticBlock(statement_list) } TokenKind::Punctuator(Punctuator::Mul) => { let token = cursor.peek(1, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -701,31 +683,29 @@ where cursor.set_strict_mode(strict); match class_element_name { - node::object::ClassElementName::PropertyName(property_name) if r#static => { + ClassElementName::PropertyName(property_name) if r#static => { if let Some(Sym::PROTOTYPE) = property_name.prop_name() { return Err(ParseError::general( "class may not have static method definitions named 'prototype'", name_position, )); } - ClassElementNode::StaticMethodDefinition(property_name, method) + function::ClassElement::StaticMethodDefinition(property_name, method) } - node::object::ClassElementName::PropertyName(property_name) => { - ClassElementNode::MethodDefinition(property_name, method) + ClassElementName::PropertyName(property_name) => { + function::ClassElement::MethodDefinition(property_name, method) } - node::object::ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) => { + ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) => { return Err(ParseError::general( "class constructor may not be a private method", name_position, )) } - node::object::ClassElementName::PrivateIdentifier(private_ident) - if r#static => - { - ClassElementNode::PrivateStaticMethodDefinition(private_ident, method) + ClassElementName::PrivateIdentifier(private_ident) if r#static => { + function::ClassElement::PrivateStaticMethodDefinition(private_ident, method) } - node::object::ClassElementName::PrivateIdentifier(private_ident) => { - ClassElementNode::PrivateMethodDefinition(private_ident, method) + ClassElementName::PrivateIdentifier(private_ident) => { + function::ClassElement::PrivateMethodDefinition(private_ident, method) } } } @@ -767,19 +747,25 @@ where name_position, )); } - ClassElementNode::StaticMethodDefinition(property_name, method) + function::ClassElement::StaticMethodDefinition( + property_name, + method, + ) } ClassElementName::PropertyName(property_name) => { - ClassElementNode::MethodDefinition(property_name, method) + function::ClassElement::MethodDefinition(property_name, method) } ClassElementName::PrivateIdentifier(private_ident) if r#static => { - ClassElementNode::PrivateStaticMethodDefinition( + function::ClassElement::PrivateStaticMethodDefinition( private_ident, method, ) } ClassElementName::PrivateIdentifier(private_ident) => { - ClassElementNode::PrivateMethodDefinition(private_ident, method) + function::ClassElement::PrivateMethodDefinition( + private_ident, + method, + ) } } } @@ -806,10 +792,13 @@ where name_position, )); } - ClassElementNode::StaticMethodDefinition(property_name, method) + function::ClassElement::StaticMethodDefinition( + property_name, + method, + ) } ClassElementName::PropertyName(property_name) => { - ClassElementNode::MethodDefinition(property_name, method) + function::ClassElement::MethodDefinition(property_name, method) } ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) if r#static => { return Err(ParseError::general( @@ -818,10 +807,12 @@ where )) } ClassElementName::PrivateIdentifier(identifier) if r#static => { - ClassElementNode::PrivateStaticMethodDefinition(identifier, method) + function::ClassElement::PrivateStaticMethodDefinition( + identifier, method, + ) } ClassElementName::PrivateIdentifier(identifier) => { - ClassElementNode::PrivateMethodDefinition(identifier, method) + function::ClassElement::PrivateMethodDefinition(identifier, method) } } } @@ -866,11 +857,11 @@ where ))); } cursor.set_strict_mode(strict); - let method = MethodDefinition::Get(FunctionExpr::new(None, params, body)); + let method = MethodDefinition::Get(Function::new(None, params, body)); if r#static { - ClassElementNode::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition(name, method) } else { - ClassElementNode::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition(name, method) } } TokenKind::Identifier(Sym::CONSTRUCTOR) => { @@ -913,7 +904,7 @@ where interner, )?; - let method = MethodDefinition::Get(FunctionExpr::new( + let method = MethodDefinition::Get(Function::new( None, FormalParameterList::empty(), body, @@ -927,17 +918,23 @@ where )); } } - ClassElementNode::StaticMethodDefinition(name, method) + function::ClassElement::StaticMethodDefinition(name, method) } else { - ClassElementNode::MethodDefinition(name, method) + function::ClassElement::MethodDefinition(name, method) } } _ => { cursor.expect_semicolon("expected semicolon", interner)?; if r#static { - ClassElementNode::StaticFieldDefinition(Literal(Sym::GET), None) + function::ClassElement::StaticFieldDefinition( + ast::property::PropertyName::Literal(Sym::GET), + None, + ) } else { - ClassElementNode::FieldDefinition(Literal(Sym::GET), None) + function::ClassElement::FieldDefinition( + ast::property::PropertyName::Literal(Sym::GET), + None, + ) } } } @@ -981,11 +978,11 @@ where ))); } cursor.set_strict_mode(strict); - let method = MethodDefinition::Set(FunctionExpr::new(None, params, body)); + let method = MethodDefinition::Set(Function::new(None, params, body)); if r#static { - ClassElementNode::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition(name, method) } else { - ClassElementNode::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition(name, method) } } TokenKind::Identifier(Sym::CONSTRUCTOR) => { @@ -1029,7 +1026,7 @@ where ))); } cursor.set_strict_mode(strict); - let method = MethodDefinition::Set(FunctionExpr::new(None, params, body)); + let method = MethodDefinition::Set(Function::new(None, params, body)); if r#static { if let Some(name) = name.prop_name() { if name == Sym::PROTOTYPE { @@ -1039,17 +1036,23 @@ where )); } } - ClassElementNode::StaticMethodDefinition(name, method) + function::ClassElement::StaticMethodDefinition(name, method) } else { - ClassElementNode::MethodDefinition(name, method) + function::ClassElement::MethodDefinition(name, method) } } _ => { cursor.expect_semicolon("expected semicolon", interner)?; if r#static { - ClassElementNode::StaticFieldDefinition(Literal(Sym::SET), None) + function::ClassElement::StaticFieldDefinition( + ast::property::PropertyName::Literal(Sym::SET), + None, + ) } else { - ClassElementNode::FieldDefinition(Literal(Sym::SET), None) + function::ClassElement::FieldDefinition( + ast::property::PropertyName::Literal(Sym::SET), + None, + ) } } } @@ -1079,9 +1082,9 @@ where cursor.expect_semicolon("expected semicolon", interner)?; cursor.set_strict_mode(strict); if r#static { - ClassElementNode::PrivateStaticFieldDefinition(name, Some(rhs)) + function::ClassElement::PrivateStaticFieldDefinition(name, Some(rhs)) } else { - ClassElementNode::PrivateFieldDefinition(name, Some(rhs)) + function::ClassElement::PrivateFieldDefinition(name, Some(rhs)) } } TokenKind::Punctuator(Punctuator::OpenParen) => { @@ -1110,21 +1113,20 @@ where token.span().start(), ))); } - let method = - MethodDefinition::Ordinary(FunctionExpr::new(None, params, body)); + let method = MethodDefinition::Ordinary(Function::new(None, params, body)); cursor.set_strict_mode(strict); if r#static { - ClassElementNode::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition(name, method) } else { - ClassElementNode::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition(name, method) } } _ => { cursor.expect_semicolon("expected semicolon", interner)?; if r#static { - ClassElementNode::PrivateStaticFieldDefinition(name, None) + function::ClassElement::PrivateStaticFieldDefinition(name, None) } else { - ClassElementNode::PrivateFieldDefinition(name, None) + function::ClassElement::PrivateFieldDefinition(name, None) } } } @@ -1169,9 +1171,9 @@ where cursor.expect_semicolon("expected semicolon", interner)?; cursor.set_strict_mode(strict); if r#static { - ClassElementNode::StaticFieldDefinition(name, Some(rhs)) + function::ClassElement::StaticFieldDefinition(name, Some(rhs)) } else { - ClassElementNode::FieldDefinition(name, Some(rhs)) + function::ClassElement::FieldDefinition(name, Some(rhs)) } } TokenKind::Punctuator(Punctuator::OpenParen) => { @@ -1208,13 +1210,12 @@ where token.span().start(), ))); } - let method = - MethodDefinition::Ordinary(FunctionExpr::new(None, params, body)); + let method = MethodDefinition::Ordinary(Function::new(None, params, body)); cursor.set_strict_mode(strict); if r#static { - ClassElementNode::StaticMethodDefinition(name, method) + function::ClassElement::StaticMethodDefinition(name, method) } else { - ClassElementNode::MethodDefinition(name, method) + function::ClassElement::MethodDefinition(name, method) } } _ => { @@ -1235,9 +1236,9 @@ where } cursor.expect_semicolon("expected semicolon", interner)?; if r#static { - ClassElementNode::StaticFieldDefinition(name, None) + function::ClassElement::StaticFieldDefinition(name, None) } else { - ClassElementNode::FieldDefinition(name, None) + function::ClassElement::FieldDefinition(name, None) } } } @@ -1253,10 +1254,10 @@ where match &element { // FieldDefinition : ClassElementName Initializer [opt] // It is a Syntax Error if Initializer is present and ContainsArguments of Initializer is true. - ClassElementNode::FieldDefinition(_, Some(node)) - | ClassElementNode::StaticFieldDefinition(_, Some(node)) - | ClassElementNode::PrivateFieldDefinition(_, Some(node)) - | ClassElementNode::PrivateStaticFieldDefinition(_, Some(node)) => { + function::ClassElement::FieldDefinition(_, Some(node)) + | function::ClassElement::StaticFieldDefinition(_, Some(node)) + | function::ClassElement::PrivateFieldDefinition(_, Some(node)) + | function::ClassElement::PrivateStaticFieldDefinition(_, Some(node)) => { if node.contains_arguments() { return Err(ParseError::general( "'arguments' not allowed in class field definition", @@ -1267,8 +1268,8 @@ where // ClassStaticBlockBody : ClassStaticBlockStatementList // It is a Syntax Error if ContainsArguments of ClassStaticBlockStatementList is true. // It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true. - ClassElementNode::StaticBlock(block) => { - for node in block.items() { + function::ClassElement::StaticBlock(block) => { + for node in block.statements() { if node.contains_arguments() { return Err(ParseError::general( "'arguments' not allowed in class static block", 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 52edab5830f..6d5d918bc35 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,31 +1,25 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - declaration::class_decl::ClassElement as ClassElementNode, - object::{MethodDefinition, PropertyName}, - Class, FormalParameterList, FunctionExpr, Node, - }, - Const, - }, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + function::{Class, ClassElement, FormalParameterList, Function}, + property::{MethodDefinition, PropertyName}, + statement::StatementList, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn check_async_ordinary_method() { let mut interner = Interner::default(); - let elements = vec![ClassElementNode::MethodDefinition( - PropertyName::Computed(Node::Const(Const::from( - interner.get_or_intern_static("async", utf16!("async")), - ))), - MethodDefinition::Ordinary(FunctionExpr::new( + let elements = vec![ClassElement::MethodDefinition( + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), + MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), - vec![], + StatementList::default(), )), )]; @@ -34,12 +28,13 @@ fn check_async_ordinary_method() { async() { } } ", - [Node::ClassDecl(Class::new( - interner.get_or_intern_static("A", utf16!("A")), + [Class::new( + Some(interner.get_or_intern_static("A", utf16!("A"))), None, None, - elements, - ))], + elements.into(), + ) + .into()], interner, ); } @@ -48,11 +43,9 @@ fn check_async_ordinary_method() { fn check_async_field_initialization() { let mut interner = Interner::default(); - let elements = vec![ClassElementNode::FieldDefinition( - PropertyName::Computed(Node::Const(Const::from( - interner.get_or_intern_static("async", utf16!("async")), - ))), - Some(Node::Const(Const::from(1))), + let elements = vec![ClassElement::FieldDefinition( + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), + Some(Literal::from(1).into()), )]; check_parser( @@ -61,12 +54,13 @@ fn check_async_field_initialization() { = 1 } ", - [Node::ClassDecl(Class::new( - interner.get_or_intern_static("A", utf16!("A")), + [Class::new( + Some(interner.get_or_intern_static("A", utf16!("A"))), None, None, - elements, - ))], + elements.into(), + ) + .into()], interner, ); } @@ -75,10 +69,8 @@ fn check_async_field_initialization() { fn check_async_field() { let mut interner = Interner::default(); - let elements = vec![ClassElementNode::FieldDefinition( - PropertyName::Computed(Node::Const(Const::from( - interner.get_or_intern_static("async", utf16!("async")), - ))), + let elements = vec![ClassElement::FieldDefinition( + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), None, )]; @@ -87,12 +79,13 @@ fn check_async_field() { async } ", - [Node::ClassDecl(Class::new( - interner.get_or_intern_static("A", utf16!("A")), + [Class::new( + interner.get_or_intern_static("A", utf16!("A")).into(), None, None, - elements, - ))], + elements.into(), + ) + .into()], interner, ); } 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 d8010459401..7c237367ef0 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 @@ -2,10 +2,10 @@ mod tests; use crate::syntax::{ - ast::{node::FunctionDecl, Keyword}, + ast::{function::Function, Keyword}, parser::{ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -78,17 +78,13 @@ impl TokenParser for FunctionDeclaration where R: Read, { - type Output = FunctionDecl; + type Output = Function; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect((Keyword::Function, false), "function declaration", interner)?; let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(FunctionDecl::new(result.0, result.1, result.2)) + Ok(Function::new(Some(result.0), result.1, result.2)) } } 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 fe121a24a7c..fdf8aedef09 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,11 +1,12 @@ -use crate::{ - string::utf16, - syntax::{ - ast::node::{FormalParameterList, FunctionDecl}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + function::{FormalParameterList, Function}, + statement::StatementList, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; /// Function declaration parsing. #[test] @@ -13,10 +14,10 @@ fn function_declaration() { let mut interner = Interner::default(); check_parser( "function hello() {}", - vec![FunctionDecl::new( - interner.get_or_intern_static("hello", utf16!("hello")), + vec![Function::new( + Some(interner.get_or_intern_static("hello", utf16!("hello"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, @@ -28,10 +29,10 @@ fn function_declaration() { fn function_declaration_keywords() { macro_rules! genast { ($keyword:literal, $interner:expr) => { - vec![FunctionDecl::new( - $interner.get_or_intern_static($keyword, utf16!($keyword)), + vec![Function::new( + Some($interner.get_or_intern_static($keyword, utf16!($keyword))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()] }; 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 7e546b6388a..d991cc0cca0 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 @@ -2,10 +2,11 @@ mod tests; use crate::syntax::{ - ast::{node::declaration::generator_decl::GeneratorDecl, Keyword, Punctuator}, + ast::function::Generator, + ast::{Keyword, Punctuator}, parser::{ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -73,13 +74,9 @@ impl TokenParser for GeneratorDeclaration where R: Read, { - type Output = GeneratorDecl; + type Output = Generator; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect( (Keyword::Function, false), "generator declaration", @@ -89,6 +86,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(GeneratorDecl::new(result.0, result.1, result.2)) + Ok(Generator::new(Some(result.0), result.1, result.2)) } } 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 55a46cc2214..66086920ab3 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,21 +1,22 @@ -use crate::{ - string::utf16, - syntax::{ - ast::node::{FormalParameterList, GeneratorDecl}, - parser::tests::check_parser, +use crate::syntax::{ + ast::{ + function::{FormalParameterList, Generator}, + statement::StatementList, }, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn generator_function_declaration() { let mut interner = Interner::default(); check_parser( "function* gen() {}", - vec![GeneratorDecl::new( - interner.get_or_intern_static("gen", utf16!("gen")), + vec![Generator::new( + Some(interner.get_or_intern_static("gen", utf16!("gen"))), FormalParameterList::default(), - vec![], + StatementList::default(), ) .into()], interner, 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 a638c6d9a35..0f042882892 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -20,8 +20,11 @@ use self::{ class_decl::ClassDeclaration, generator_decl::GeneratorDeclaration, }; use crate::syntax::{ - ast::node::{FormalParameterList, StatementList}, - ast::{node::function_contains_super, Keyword, Node, Position, Punctuator}, + ast::statement::StatementList, + ast::{ + function::{function_contains_super, FormalParameterList}, + Keyword, Position, Punctuator, Statement, + }, lexer::TokenKind, parser::{ expression::BindingIdentifier, @@ -69,9 +72,9 @@ impl TokenParser for HoistableDeclaration where R: Read, { - type Output = Node; + type Output = Statement; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("HoistableDeclaration", "Parsing"); let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -87,11 +90,11 @@ where if let TokenKind::Punctuator(Punctuator::Mul) = next_token.kind() { GeneratorDeclaration::new(self.allow_yield, self.allow_await, self.is_default) .parse(cursor, interner) - .map(Node::from) + .map(Statement::from) } else { FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default) .parse(cursor, interner) - .map(Node::from) + .map(Statement::from) } } TokenKind::Keyword((Keyword::Async, false)) => { @@ -103,17 +106,17 @@ where self.is_default, ) .parse(cursor, interner) - .map(Node::from) + .map(Statement::from) } else { AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false) .parse(cursor, interner) - .map(Node::from) + .map(Statement::from) } } TokenKind::Keyword((Keyword::Class, false)) => { ClassDeclaration::new(self.allow_yield, self.allow_await, false) .parse(cursor, interner) - .map(Node::from) + .map(Statement::from) } _ => unreachable!("unknown token found: {:?}", tok), } diff --git a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs index 40609656579..a5c65705b6d 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs @@ -9,12 +9,8 @@ use crate::syntax::{ ast::{ - node::{ - declaration::{ - Declaration, DeclarationList, DeclarationPatternArray, DeclarationPatternObject, - }, - DeclarationPattern, Node, - }, + pattern::Pattern, + statement::declaration::{Declaration, DeclarationList}, Keyword, Punctuator, }, lexer::{Error as LexError, TokenKind}, @@ -36,20 +32,20 @@ use std::io::Read; /// /// [spec]: https://tc39.es/ecma262/#prod-LexicalDeclaration #[derive(Debug, Clone, Copy)] -pub(super) struct LexicalDeclaration { +pub(in crate::syntax::parser) struct LexicalDeclaration { allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, - const_init_required: bool, + loop_init: bool, } impl LexicalDeclaration { /// Creates a new `LexicalDeclaration` parser. - pub(super) fn new( + pub(in crate::syntax::parser) fn new( allow_in: I, allow_yield: Y, allow_await: A, - const_init_required: bool, + loop_init: bool, ) -> Self where I: Into, @@ -60,7 +56,7 @@ impl LexicalDeclaration { allow_in: allow_in.into(), allow_yield: allow_yield.into(), allow_await: allow_await.into(), - const_init_required, + loop_init, } } } @@ -69,9 +65,9 @@ impl TokenParser for LexicalDeclaration where R: Read, { - type Output = Node; + type Output = DeclarationList; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("LexicalDeclaration", "Parsing"); let tok = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; @@ -85,7 +81,7 @@ where self.allow_yield, self.allow_await, true, - self.const_init_required, + self.loop_init, ) .parse(cursor, interner), TokenKind::Keyword((Keyword::Let, false)) => BindingList::new( @@ -93,7 +89,7 @@ where self.allow_yield, self.allow_await, false, - self.const_init_required, + self.loop_init, ) .parse(cursor, interner), _ => unreachable!("unknown token found: {:?}", tok), @@ -116,7 +112,7 @@ struct BindingList { allow_yield: AllowYield, allow_await: AllowAwait, is_const: bool, - const_init_required: bool, + loop_init: bool, } impl BindingList { @@ -126,7 +122,7 @@ impl BindingList { allow_yield: Y, allow_await: A, is_const: bool, - const_init_required: bool, + loop_init: bool, ) -> Self where I: Into, @@ -138,7 +134,7 @@ impl BindingList { allow_yield: allow_yield.into(), allow_await: allow_await.into(), is_const, - const_init_required, + loop_init, } } } @@ -147,42 +143,33 @@ impl TokenParser for BindingList where R: Read, { - type Output = Node; + type Output = DeclarationList; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("BindingList", "Parsing"); // Create vectors to store the variable declarations // Const and Let signatures are slightly different, Const needs definitions, Lets don't - let mut let_decls = Vec::new(); - let mut const_decls = Vec::new(); + let mut decls = Vec::new(); loop { let decl = LexicalBinding::new(self.allow_in, self.allow_yield, self.allow_await) .parse(cursor, interner)?; if self.is_const { - if self.const_init_required { - let init_is_some = match &decl { - Declaration::Identifier { init, .. } if init.is_some() => true, - Declaration::Pattern(p) if p.init().is_some() => true, - _ => false, - }; - - if init_is_some { - const_decls.push(decl); - } else { - let next = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; - return Err(ParseError::general( - "Expected initializer for const declaration", - next.span().start(), - )); - } + let init_is_some = decl.init().is_some(); + + if init_is_some || self.loop_init { + decls.push(decl); } else { - const_decls.push(decl); + let next = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; + return Err(ParseError::general( + "Expected initializer for const declaration", + next.span().start(), + )); } } else { - let_decls.push(decl); + decls.push(decl); } match cursor.peek_semicolon(interner)? { @@ -208,6 +195,7 @@ where // We discard the comma let _comma = cursor.next(interner)?; } + SemicolonResult::NotFound(_) if self.loop_init => break, SemicolonResult::NotFound(_) => { let next = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; return Err(ParseError::expected( @@ -221,9 +209,9 @@ where } if self.is_const { - Ok(DeclarationList::Const(const_decls.into()).into()) + Ok(DeclarationList::Const(decls.into())) } else { - Ok(DeclarationList::Let(let_decls.into()).into()) + Ok(DeclarationList::Let(decls.into())) } } } @@ -262,11 +250,7 @@ where { type Output = Declaration; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("LexicalBinding", "Parsing"); let peek_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -295,8 +279,7 @@ where None }; - let declaration = - DeclarationPattern::Object(DeclarationPatternObject::new(bindings, init)); + let declaration = Pattern::Object(bindings.into()); if declaration.idents().contains(&Sym::LET) { return Err(ParseError::lex(LexError::Syntax( @@ -305,7 +288,7 @@ where ))); } - Ok(Declaration::Pattern(declaration)) + Ok(Declaration::from_pattern(declaration, init)) } TokenKind::Punctuator(Punctuator::OpenBracket) => { let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await) @@ -329,8 +312,7 @@ where None }; - let declaration = - DeclarationPattern::Array(DeclarationPatternArray::new(bindings, init)); + let declaration = Pattern::Array(bindings.into()); if declaration.idents().contains(&Sym::LET) { return Err(ParseError::lex(LexError::Syntax( @@ -339,7 +321,7 @@ where ))); } - Ok(Declaration::Pattern(declaration)) + Ok(Declaration::from_pattern(declaration, init)) } _ => { let ident = BindingIdentifier::new(self.allow_yield, self.allow_await) @@ -355,8 +337,13 @@ where let init = if let Some(t) = cursor.peek(0, interner)? { if *t.kind() == TokenKind::Punctuator(Punctuator::Assign) { Some( - Initializer::new(Some(ident), true, self.allow_yield, self.allow_await) - .parse(cursor, interner)?, + Initializer::new( + Some(ident), + self.allow_in, + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner)?, ) } else { None @@ -364,7 +351,7 @@ where } else { None }; - Ok(Declaration::new_with_identifier(ident, init)) + Ok(Declaration::from_identifier(ident.into(), init)) } } } diff --git a/boa_engine/src/syntax/parser/statement/declaration/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/mod.rs index 70a7bc8e965..d1d6134dacb 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/mod.rs @@ -7,24 +7,26 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#Declarations //! [spec]:https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement -pub(in crate::syntax::parser) mod hoistable; +mod hoistable; mod lexical; #[cfg(test)] mod tests; -use self::{hoistable::HoistableDeclaration, lexical::LexicalDeclaration}; +pub(in crate::syntax::parser) use hoistable::class_decl::ClassTail; +pub(in crate::syntax) use hoistable::class_decl::PrivateElement; +pub(in crate::syntax::parser) use hoistable::FunctionDeclaration; +use hoistable::HoistableDeclaration; +pub(in crate::syntax::parser) use lexical::LexicalDeclaration; + use crate::syntax::{ - ast::{Keyword, Node}, + ast::{Keyword, Statement}, lexer::TokenKind, - parser::{AllowAwait, AllowYield, Cursor, ParseError, TokenParser}, + parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; -pub(in crate::syntax::parser) use hoistable::class_decl::ClassTail; -pub(in crate::syntax) use hoistable::class_decl::PrivateElement; - /// Parses a declaration. /// /// More information: @@ -35,11 +37,10 @@ pub(in crate::syntax) use hoistable::class_decl::PrivateElement; pub(super) struct Declaration { allow_yield: AllowYield, allow_await: AllowAwait, - const_init_required: bool, } impl Declaration { - pub(super) fn new(allow_yield: Y, allow_await: A, const_init_required: bool) -> Self + pub(super) fn new(allow_yield: Y, allow_await: A) -> Self where Y: Into, A: Into, @@ -47,7 +48,6 @@ impl Declaration { Self { allow_yield: allow_yield.into(), allow_await: allow_await.into(), - const_init_required, } } } @@ -56,13 +56,9 @@ impl TokenParser for Declaration where R: Read, { - type Output = Node; + type Output = Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Declaration", "Parsing"); let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -71,13 +67,11 @@ where HoistableDeclaration::new(self.allow_yield, self.allow_await, false) .parse(cursor, interner) } - TokenKind::Keyword((Keyword::Const | Keyword::Let, _)) => LexicalDeclaration::new( - true, - self.allow_yield, - self.allow_await, - self.const_init_required, - ) - .parse(cursor, interner), + TokenKind::Keyword((Keyword::Const | Keyword::Let, _)) => { + LexicalDeclaration::new(true, self.allow_yield, self.allow_await, false) + .parse(cursor, interner) + .map(Into::into) + } _ => unreachable!("unknown token found: {:?}", tok), } } diff --git a/boa_engine/src/syntax/parser/statement/declaration/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/tests.rs index d5461180919..33aa0a3c3c2 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/tests.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/tests.rs @@ -1,14 +1,12 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{Declaration, DeclarationList, Node}, - Const, - }, - parser::tests::{check_invalid, check_parser}, +use crate::syntax::{ + ast::{ + expression::literal::Literal, + statement::declaration::{Declaration, DeclarationList}, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::Interner; +use boa_macros::utf16; /// Checks `var` declaration parsing. #[test] @@ -17,9 +15,9 @@ fn var_declaration() { check_parser( "var a = 5;", vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::from(5).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -35,9 +33,11 @@ fn var_declaration_keywords() { check_parser( "var yield = 5;", vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("yield", utf16!("yield")), - Some(Const::from(5).into()), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("yield", utf16!("yield")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -49,9 +49,11 @@ fn var_declaration_keywords() { check_parser( "var await = 5;", vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("await", utf16!("await")), - Some(Const::from(5).into()), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("await", utf16!("await")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -67,9 +69,9 @@ fn var_declaration_no_spaces() { check_parser( "var a=5;", vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::from(5).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -85,8 +87,8 @@ fn empty_var_declaration() { check_parser( "var a;", vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, )] .into(), @@ -104,17 +106,17 @@ fn multiple_var_declaration() { "var a = 5, b, c = 6;", vec![DeclarationList::Var( vec![ - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::from(5).into()), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), ), - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), - Some(Const::from(6).into()), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), + Some(Literal::from(6).into()), ), ] .into(), @@ -131,9 +133,9 @@ fn let_declaration() { check_parser( "let a = 5;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -149,9 +151,11 @@ fn let_declaration_keywords() { check_parser( "let yield = 5;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("yield", utf16!("yield")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("yield", utf16!("yield")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -163,9 +167,11 @@ fn let_declaration_keywords() { check_parser( "let await = 5;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("await", utf16!("await")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("await", utf16!("await")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -181,9 +187,9 @@ fn let_declaration_no_spaces() { check_parser( "let a=5;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -199,8 +205,8 @@ fn empty_let_declaration() { check_parser( "let a;", vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), None, )] .into(), @@ -218,17 +224,17 @@ fn multiple_let_declaration() { "let a = 5, b, c = 6;", vec![DeclarationList::Let( vec![ - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), ), - Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), + Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), None, ), - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), - Node::from(Const::from(6)), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), + Some(Literal::from(6).into()), ), ] .into(), @@ -245,9 +251,9 @@ fn const_declaration() { check_parser( "const a = 5;", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -263,9 +269,11 @@ fn const_declaration_keywords() { check_parser( "const yield = 5;", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("yield", utf16!("yield")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("yield", utf16!("yield")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -277,9 +285,11 @@ fn const_declaration_keywords() { check_parser( "const await = 5;", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("await", utf16!("await")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner + .get_or_intern_static("await", utf16!("await")) + .into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -295,9 +305,9 @@ fn const_declaration_no_spaces() { check_parser( "const a=5;", vec![DeclarationList::Const( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), )] .into(), ) @@ -320,13 +330,13 @@ fn multiple_const_declaration() { "const a = 5, c = 6;", vec![DeclarationList::Const( vec![ - Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Node::from(Const::from(5)), + Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::from(5).into()), ), - Declaration::new_with_identifier( - interner.get_or_intern_static("c", utf16!("c")), - Node::from(Const::from(6)), + Declaration::from_identifier( + interner.get_or_intern_static("c", utf16!("c")).into(), + Some(Literal::from(6).into()), ), ] .into(), diff --git a/boa_engine/src/syntax/parser/statement/expression/mod.rs b/boa_engine/src/syntax/parser/statement/expression/mod.rs index 2ce1fa02bf1..57f7ee33435 100644 --- a/boa_engine/src/syntax/parser/statement/expression/mod.rs +++ b/boa_engine/src/syntax/parser/statement/expression/mod.rs @@ -1,5 +1,5 @@ use crate::syntax::{ - ast::{node::Node, Keyword, Punctuator}, + ast::{Keyword, Punctuator, Statement}, lexer::TokenKind, parser::{ expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, @@ -40,9 +40,9 @@ impl TokenParser for ExpressionStatement where R: Read, { - type Output = Node; + type Output = Statement; - fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ExpressionStatement", "Parsing"); let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -97,6 +97,6 @@ where cursor.expect_semicolon("expression statement", interner)?; - Ok(expr) + Ok(expr.into()) } } diff --git a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs index 66e051c0e27..9cb60f66c87 100644 --- a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs @@ -2,18 +2,19 @@ mod tests; use crate::syntax::{ - ast::{node::If, Keyword, Node, Punctuator}, + ast::{self, statement::If, Keyword, Punctuator}, lexer::TokenKind, parser::{ - expression::Expression, - statement::{declaration::hoistable::FunctionDeclaration, Statement}, - AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + expression::Expression, statement::declaration::FunctionDeclaration, AllowAwait, + AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; +use super::Statement; + /// If statement parsing. /// /// An _If_ statement will have a condition, a block statemet, and an optional _else_ statement. @@ -53,11 +54,7 @@ where { type Output = If; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("IfStatement", "Parsing"); cursor.expect((Keyword::If, false), "if statement", interner)?; @@ -86,7 +83,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true. - if let Node::FunctionDecl(_) = node { + if let ast::Statement::Function(_) = node { return Err(ParseError::wrong_function_declaration_non_strict(position)); } @@ -126,7 +123,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(the second Statement) is true. - if let Node::FunctionDecl(_) = node { + if let ast::Statement::Function(_) = node { return Err(ParseError::wrong_function_declaration_non_strict( position, )); @@ -142,6 +139,6 @@ where None }; - Ok(If::new::<_, _, Node, _>(condition, then_node, else_node)) + Ok(If::new(condition, then_node, else_node)) } } 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 5328e5a6847..ae64aaeb6da 100644 --- a/boa_engine/src/syntax/parser/statement/if_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/if_stm/tests.rs @@ -1,7 +1,7 @@ use crate::syntax::{ ast::{ - node::{Block, If, Node}, - Const, + expression::literal::Literal, + statement::{Block, If}, }, parser::tests::check_parser, }; @@ -11,7 +11,12 @@ use boa_interner::Interner; fn if_without_else_block() { check_parser( "if (true) {}", - vec![If::new::<_, _, Node, _>(Const::from(true), Block::from(Vec::new()), None).into()], + vec![If::new( + Literal::from(true).into(), + Block::from(Vec::new()).into(), + None, + ) + .into()], Interner::default(), ); } @@ -20,7 +25,12 @@ fn if_without_else_block() { fn if_without_else_block_with_trailing_newline() { check_parser( "if (true) {}\n", - vec![If::new::<_, _, Node, _>(Const::from(true), Block::from(Vec::new()), None).into()], + vec![If::new( + Literal::from(true).into(), + Block::from(Vec::new()).into(), + None, + ) + .into()], Interner::default(), ); } 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 81afce10fda..0b953e6c0d8 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,11 +8,11 @@ //! [spec]: https://tc39.es/ecma262/#sec-do-while-statement use crate::syntax::{ - ast::{node::DoWhileLoop, Keyword, Node, Punctuator}, + ast::{self, statement::DoWhileLoop, Keyword, Punctuator}, lexer::TokenKind, parser::{ expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, - ParseError, TokenParser, + ParseError, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -60,11 +60,7 @@ where { type Output = DoWhileLoop; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("DoWhileStatement", "Parsing"); let position = cursor @@ -76,7 +72,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true. - if let Node::FunctionDecl(_) = body { + if let ast::Statement::Function(_) = body { return Err(ParseError::wrong_function_declaration_non_strict(position)); } 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 de53269d33d..0080687583b 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs @@ -9,21 +9,20 @@ use crate::syntax::{ ast::{ - node::{ - iteration::IterableLoopInitializer, - operator::assign::{ - array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, - }, - ForInLoop, ForLoop, ForOfLoop, Node, + self, + statement::{ + declaration::DeclarationList, + iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, + ForInLoop, ForLoop, ForOfLoop, }, - Const, Keyword, Position, Punctuator, + Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ expression::Expression, - statement::declaration::Declaration, + statement::declaration::LexicalDeclaration, statement::{variable::VariableDeclarationList, Statement}, - AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -70,13 +69,9 @@ impl TokenParser for ForStatement where R: Read, { - type Output = Node; + type Output = ast::Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ForStatement", "Parsing"); cursor.expect((Keyword::For, false), "for statement", interner)?; @@ -117,13 +112,14 @@ where let _next = cursor.next(interner)?; Some( VariableDeclarationList::new(false, self.allow_yield, self.allow_await) - .parse(cursor, interner) - .map(Node::from)?, + .parse(cursor, interner)? + .into(), ) } TokenKind::Keyword((Keyword::Let | Keyword::Const, _)) => Some( - Declaration::new(self.allow_yield, self.allow_await, false) - .parse(cursor, interner)?, + LexicalDeclaration::new(false, self.allow_yield, self.allow_await, true) + .parse(cursor, interner)? + .into(), ), TokenKind::Keyword((Keyword::Async, false)) => { match cursor @@ -139,29 +135,31 @@ where } _ => Some( Expression::new(None, false, self.allow_yield, self.allow_await) - .parse(cursor, interner)?, + .parse(cursor, interner)? + .into(), ), } } TokenKind::Punctuator(Punctuator::Semicolon) => None, _ => Some( Expression::new(None, false, self.allow_yield, self.allow_await) - .parse(cursor, interner)?, + .parse(cursor, interner)? + .into(), ), }; let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; - match (init.as_ref(), token.kind()) { + let position = token.span().start(); + let init = match (init, token.kind()) { (Some(_), TokenKind::Keyword((Keyword::In | Keyword::Of, true))) => { return Err(ParseError::general( "Keyword must not contain escaped characters", - token.span().start(), + position, )); } (Some(init), TokenKind::Keyword((Keyword::In, false))) => { - let init_position = token.span().start(); let init = - node_to_iterable_loop_initializer(init, init_position, cursor.strict_mode())?; + initializer_to_iterable_loop_initializer(init, position, cursor.strict_mode())?; let _next = cursor.next(interner)?; let expr = Expression::new(None, true, self.allow_yield, self.allow_await) @@ -176,7 +174,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true. - if let Node::FunctionDecl(_) = body { + if let ast::Statement::Function(_) = body { return Err(ParseError::wrong_function_declaration_non_strict(position)); } @@ -190,19 +188,19 @@ where if name == Sym::LET { return Err(ParseError::general( "Cannot use 'let' as a lexically bound name", - init_position, + position, )); } if vars.contains(&name) { return Err(ParseError::general( "For loop initializer declared in loop body", - init_position, + position, )); } if !bound_names.insert(name) { return Err(ParseError::general( "For loop initializer cannot contain duplicate identifiers", - init_position, + position, )); } } @@ -211,7 +209,7 @@ where } (Some(init), TokenKind::Keyword((Keyword::Of, false))) => { let init = - node_to_iterable_loop_initializer(init, init_position, cursor.strict_mode())?; + initializer_to_iterable_loop_initializer(init, position, cursor.strict_mode())?; let _next = cursor.next(interner)?; let iterable = Expression::new(None, true, self.allow_yield, self.allow_await) @@ -226,7 +224,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true. - if let Node::FunctionDecl(_) = body { + if let ast::Statement::Function(_) = body { return Err(ParseError::wrong_function_declaration_non_strict(position)); } @@ -240,49 +238,48 @@ where if name == Sym::LET { return Err(ParseError::general( "Cannot use 'let' as a lexically bound name", - init_position, + position, )); } if vars.contains(&name) { return Err(ParseError::general( "For loop initializer declared in loop body", - init_position, + position, )); } if !bound_names.insert(name) { return Err(ParseError::general( "For loop initializer cannot contain duplicate identifiers", - init_position, + position, )); } } return Ok(ForOfLoop::new(init, iterable, body, r#await).into()); } - (Some(Node::ConstDeclList(list)), _) => { - // Reject const declarations without initializers inside for loops - for decl in list.as_ref() { - if decl.init().is_none() { - return Err(ParseError::general( - "Expected initializer for const declaration", - // TODO: get exact position of uninitialized const decl - init_position, - )); - } + (init, _) => init, + }; + + if let Some(ForLoopInitializer::DeclarationList(DeclarationList::Const(ref list))) = init { + for decl in &**list { + if decl.init().is_none() { + return Err(ParseError::general( + "Expected initializer for const declaration", + position, + )); } } - _ => {} } cursor.expect(Punctuator::Semicolon, "for statement", interner)?; let cond = if cursor.next_if(Punctuator::Semicolon, interner)?.is_some() { - Const::from(true).into() + None } else { let step = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::Semicolon, "for statement", interner)?; - step + Some(step) }; let step = if cursor.next_if(Punctuator::CloseParen, interner)?.is_some() { @@ -308,98 +305,82 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true. - if let Node::FunctionDecl(_) = body { + if let ast::Statement::Function(_) = body { return Err(ParseError::wrong_function_declaration_non_strict(position)); } - // TODO: do not encapsulate the `for` in a block just to have an inner scope. + // Early Error: It is a Syntax Error if any element of the BoundNames of + // LexicalDeclaration also occurs in the VarDeclaredNames of Statement. + let mut vars = FxHashSet::default(); + body.var_declared_names(&mut vars); + if let Some(ref init) = init { + for name in init.bound_names() { + if vars.contains(&name) { + return Err(ParseError::general( + "For loop initializer declared in loop body", + position, + )); + } + } + } + Ok(ForLoop::new(init, cond, step, body).into()) } } #[inline] -fn node_to_iterable_loop_initializer( - node: &Node, +fn initializer_to_iterable_loop_initializer( + initializer: ForLoopInitializer, position: Position, strict: bool, ) -> Result { - match node { - Node::Identifier(name) => Ok(IterableLoopInitializer::Identifier(*name)), - Node::VarDeclList(ref list) => match list.as_ref() { - [var] => { - if var.init().is_some() { - return Err(ParseError::lex(LexError::Syntax( - "a declaration in the head of a for-of loop can't have an initializer" - .into(), - position, - ))); - } - Ok(IterableLoopInitializer::Var(var.clone())) - } - _ => Err(ParseError::lex(LexError::Syntax( - "only one variable can be declared in the head of a for-of loop".into(), - position, - ))), - }, - Node::LetDeclList(ref list) => match list.as_ref() { - [var] => { - if var.init().is_some() { - return Err(ParseError::lex(LexError::Syntax( - "a declaration in the head of a for-of loop can't have an initializer" - .into(), - position, - ))); - } - Ok(IterableLoopInitializer::Let(var.clone())) + match initializer { + ForLoopInitializer::Expression(expr) => match expr { + ast::Expression::Identifier(ident) + if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) => + { + Err(ParseError::lex(LexError::Syntax( + "cannot use `eval` or `arguments` as iterable loop variable in strict code" + .into(), + position, + ))) } + + ast::Expression::Identifier(ident) => Ok(IterableLoopInitializer::Identifier(ident)), + // TODO: implement member initializers + ast::Expression::PropertyAccess(_) => todo!(), + ast::Expression::SuperPropertyAccess(_) => todo!(), + ast::Expression::PrivatePropertyAccess(_) => todo!(), _ => Err(ParseError::lex(LexError::Syntax( - "only one variable can be declared in the head of a for-of loop".into(), + "invalid variable for iterable loop".into(), position, ))), }, - Node::ConstDeclList(ref list) => match list.as_ref() { - [var] => { - if var.init().is_some() { + ForLoopInitializer::DeclarationList(list) => match list.as_ref() { + [declaration] => { + if declaration.init().is_some() { return Err(ParseError::lex(LexError::Syntax( "a declaration in the head of a for-of loop can't have an initializer" .into(), position, ))); } - Ok(IterableLoopInitializer::Const(var.clone())) + Ok(match list { + DeclarationList::Const(_) => { + IterableLoopInitializer::Const(declaration.binding().clone()) + } + DeclarationList::Let(_) => { + IterableLoopInitializer::Let(declaration.binding().clone()) + } + DeclarationList::Var(_) => { + IterableLoopInitializer::Var(declaration.binding().clone()) + } + }) } _ => Err(ParseError::lex(LexError::Syntax( "only one variable can be declared in the head of a for-of loop".into(), position, ))), }, - Node::Assign(_) => Err(ParseError::lex(LexError::Syntax( - "a declaration in the head of a for-of loop can't have an initializer".into(), - position, - ))), - Node::Object(object) => { - if let Some(pattern) = object_decl_to_declaration_pattern(object, strict) { - Ok(IterableLoopInitializer::DeclarationPattern(pattern)) - } else { - Err(ParseError::lex(LexError::Syntax( - "invalid left-hand side declaration pattern in assignment head of a for-of loop".into(), - position, - ))) - } - } - Node::ArrayDecl(array) => { - if let Some(pattern) = array_decl_to_declaration_pattern(array, strict) { - Ok(IterableLoopInitializer::DeclarationPattern(pattern)) - } else { - Err(ParseError::lex(LexError::Syntax( - "invalid left-hand side declaration pattern in assignment head of a for-of loop".into(), - position, - ))) - } - } - _ => Err(ParseError::lex(LexError::Syntax( - "unknown left hand side in head of for-of loop".into(), - position, - ))), } } diff --git a/boa_engine/src/syntax/parser/statement/iteration/tests.rs b/boa_engine/src/syntax/parser/statement/iteration/tests.rs index f1cff6009b5..f375a833016 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/tests.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/tests.rs @@ -1,18 +1,24 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - field::GetConstField, BinOp, Block, Break, Call, Declaration, DeclarationList, - DoWhileLoop, Identifier, UnaryOp, WhileLoop, +use crate::syntax::{ + ast::{ + expression::{ + access::PropertyAccess, + literal::Literal, + operator::{ + assign::op::AssignOp, binary::op::RelationalOp, unary::op::UnaryOp, Assign, Binary, + Unary, }, - op::{self, AssignOp, CompOp}, - Const, + Call, Identifier, }, - parser::tests::{check_invalid, check_parser}, + statement::{ + declaration::{Declaration, DeclarationList}, + Block, Break, DoWhileLoop, WhileLoop, + }, + Expression, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::Interner; +use boa_macros::utf16; /// Checks do-while statement parsing. #[test] @@ -23,13 +29,14 @@ fn check_do_while() { a += 1; } while (true)"#, vec![DoWhileLoop::new( - Block::from(vec![BinOp::new( + Block::from(vec![Expression::from(Assign::new( AssignOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(1), - ) - .into()]), - Const::from(true), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(1).into(), + )) + .into()]) + .into(), + Literal::from(true).into(), ) .into()], interner, @@ -45,43 +52,53 @@ fn check_do_while_semicolon_insertion() { do {console.log("hello");} while(i++ < 10) console.log("end");"#, vec![ DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("i", utf16!("i")), - Some(Const::from(0).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("i", utf16!("i")).into(), + Some(Literal::from(0).into()), )] .into(), ) .into(), DoWhileLoop::new( - Block::from(vec![Call::new( - GetConstField::new( + Block::from(vec![Expression::from(Call::new( + PropertyAccess::new( Identifier::new( interner.get_or_intern_static("console", utf16!("console")), - ), + ) + .into(), interner.get_or_intern_static("log", utf16!("log")), - ), + ) + .into(), vec![ - Const::from(interner.get_or_intern_static("hello", utf16!("hello"))).into(), - ], + Literal::from(interner.get_or_intern_static("hello", utf16!("hello"))) + .into(), + ] + .into(), + )) + .into()]) + .into(), + Binary::new( + RelationalOp::LessThan.into(), + Unary::new( + UnaryOp::IncrementPost, + Identifier::new(interner.get_or_intern_static("i", utf16!("i"))).into(), + ) + .into(), + Literal::from(10).into(), ) - .into()]), - BinOp::new( - CompOp::LessThan, - UnaryOp::new( - op::UnaryOp::IncrementPost, - Identifier::new(interner.get_or_intern_static("i", utf16!("i"))), - ), - Const::from(10), - ), + .into(), ) .into(), - Call::new( - GetConstField::new( - Identifier::new(interner.get_or_intern_static("console", utf16!("console"))), + Expression::from(Call::new( + PropertyAccess::new( + Identifier::new(interner.get_or_intern_static("console", utf16!("console"))) + .into(), interner.get_or_intern_static("log", utf16!("log")), - ), - vec![Const::from(interner.get_or_intern_static("end", utf16!("end"))).into()], - ) + ) + .into(), + vec![Literal::from(interner.get_or_intern_static("end", utf16!("end"))).into()] + .into(), + )) .into(), ], interner, @@ -98,43 +115,53 @@ fn check_do_while_semicolon_insertion_no_space() { do {console.log("hello");} while(i++ < 10)console.log("end");"#, vec![ DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("i", utf16!("i")), - Some(Const::from(0).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("i", utf16!("i")).into(), + Some(Literal::from(0).into()), )] .into(), ) .into(), DoWhileLoop::new( - Block::from(vec![Call::new( - GetConstField::new( + Block::from(vec![Expression::from(Call::new( + PropertyAccess::new( Identifier::new( interner.get_or_intern_static("console", utf16!("console")), - ), + ) + .into(), interner.get_or_intern_static("log", utf16!("log")), - ), + ) + .into(), vec![ - Const::from(interner.get_or_intern_static("hello", utf16!("hello"))).into(), - ], + Literal::from(interner.get_or_intern_static("hello", utf16!("hello"))) + .into(), + ] + .into(), + )) + .into()]) + .into(), + Binary::new( + RelationalOp::LessThan.into(), + Unary::new( + UnaryOp::IncrementPost, + Identifier::new(interner.get_or_intern_static("i", utf16!("i"))).into(), + ) + .into(), + Literal::from(10).into(), ) - .into()]), - BinOp::new( - CompOp::LessThan, - UnaryOp::new( - op::UnaryOp::IncrementPost, - Identifier::new(interner.get_or_intern_static("i", utf16!("i"))), - ), - Const::from(10), - ), + .into(), ) .into(), - Call::new( - GetConstField::new( - Identifier::new(interner.get_or_intern_static("console", utf16!("console"))), + Expression::from(Call::new( + PropertyAccess::new( + Identifier::new(interner.get_or_intern_static("console", utf16!("console"))) + .into(), interner.get_or_intern_static("log", utf16!("log")), - ), - vec![Const::from(interner.get_or_intern_static("end", utf16!("end"))).into()], - ) + ) + .into(), + vec![Literal::from(interner.get_or_intern_static("end", utf16!("end"))).into()] + .into(), + )) .into(), ], interner, @@ -158,7 +185,7 @@ fn while_spaces() { break; "#, - vec![WhileLoop::new(Const::from(true), Break::new(None)).into()], + vec![WhileLoop::new(Literal::from(true).into(), Break::new(None).into()).into()], Interner::default(), ); } @@ -181,8 +208,8 @@ fn do_while_spaces() { "#, vec![DoWhileLoop::new( - Block::from(vec![Break::new(None).into()]), - Const::Bool(true), + Block::from(vec![Break::new(None).into()]).into(), + Literal::Bool(true).into(), ) .into()], Interner::default(), diff --git a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs index 0fb46dabfbd..f809af6337d 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs @@ -1,8 +1,8 @@ use crate::syntax::{ - ast::{node::WhileLoop, Keyword, Node, Punctuator}, + ast::{self, statement::WhileLoop, Keyword, Punctuator}, parser::{ expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, - ParseError, TokenParser, + ParseError, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -50,11 +50,7 @@ where { type Output = WhileLoop; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("WhileStatement", "Parsing"); cursor.expect((Keyword::While, false), "while statement", interner)?; @@ -72,7 +68,7 @@ where .parse(cursor, interner)?; // Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true. - if let Node::FunctionDecl(_) = body { + if let ast::Statement::Function(_) = body { return Err(ParseError::wrong_function_declaration_non_strict(position)); } 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 60b3c79171f..89b7f25c389 100644 --- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs @@ -1,14 +1,12 @@ use crate::syntax::{ - ast::{Keyword, Node, Punctuator}, + ast::{self, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::Cursor, error::ParseError, expression::LabelIdentifier, - statement::{ - declaration::hoistable::FunctionDeclaration, AllowAwait, AllowReturn, Statement, - }, - AllowYield, TokenParser, + statement::{declaration::FunctionDeclaration, AllowAwait, AllowReturn, Statement}, + AllowYield, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -49,13 +47,9 @@ impl TokenParser for LabelledStatement where R: Read, { - type Output = Node; + type Output = ast::Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Label", "Parsing"); let name = @@ -83,19 +77,20 @@ where _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor, interner)? }; + // TODO: create `ast::Statement::Labelled` set_label_for_node(&mut node, name); Ok(node) } } -fn set_label_for_node(node: &mut Node, name: Sym) { +fn set_label_for_node(node: &mut ast::Statement, name: Sym) { match node { - Node::ForLoop(ref mut for_loop) => for_loop.set_label(name), - Node::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name), - Node::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name), - Node::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name), - Node::WhileLoop(ref mut while_loop) => while_loop.set_label(name), - Node::Block(ref mut block) => block.set_label(name), + ast::Statement::ForLoop(ref mut for_loop) => for_loop.set_label(name), + ast::Statement::ForOfLoop(ref mut for_of_loop) => for_of_loop.set_label(name), + ast::Statement::ForInLoop(ref mut for_in_loop) => for_in_loop.set_label(name), + ast::Statement::DoWhileLoop(ref mut do_while_loop) => do_while_loop.set_label(name), + ast::Statement::WhileLoop(ref mut while_loop) => while_loop.set_label(name), + ast::Statement::Block(ref mut block) => block.set_label(name), _ => (), } } diff --git a/boa_engine/src/syntax/parser/statement/mod.rs b/boa_engine/src/syntax/parser/statement/mod.rs index b35bfe800bc..5753ef77318 100644 --- a/boa_engine/src/syntax/parser/statement/mod.rs +++ b/boa_engine/src/syntax/parser/statement/mod.rs @@ -37,21 +37,17 @@ use self::{ variable::VariableStatement, }; use super::{ - expression::PropertyName, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + expression::PropertyName, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, + TokenParser, }; use crate::syntax::{ ast::{ - node::{ - self, - declaration::{ - BindingPatternTypeArray, BindingPatternTypeObject, DeclarationPattern, - DeclarationPatternArray, DeclarationPatternObject, - }, - }, - Keyword, Node, Punctuator, + self, + pattern::{PatternArray, PatternArrayElement, PatternObjectElement}, + Keyword, Punctuator, }, lexer::{Error as LexError, InputElement, Token, TokenKind}, - parser::expression::{await_expr::AwaitExpression, BindingIdentifier, Initializer}, + parser::expression::{BindingIdentifier, Initializer}, }; use boa_interner::Interner; use boa_profiler::Profiler; @@ -113,51 +109,44 @@ impl TokenParser for Statement where R: Read, { - type Output = Node; + type Output = ast::Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Statement", "Parsing"); // TODO: add BreakableStatement and divide Whiles, fors and so on to another place. let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; match tok.kind() { - TokenKind::Keyword((Keyword::Await, _)) => AwaitExpression::new(self.allow_yield) - .parse(cursor, interner) - .map(Node::from), TokenKind::Keyword((Keyword::If, _)) => { IfStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Var, _)) => { VariableStatement::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::While, _)) => { WhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Do, _)) => { DoWhileStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::For, _)) => { ForStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Return, _)) => { if self.allow_return.0 { ReturnStatement::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } else { Err(ParseError::unexpected( tok.to_string(interner), @@ -169,37 +158,37 @@ where TokenKind::Keyword((Keyword::Break, _)) => { BreakStatement::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Continue, _)) => { ContinueStatement::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Try, _)) => { TryStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Throw, _)) => { ThrowStatement::new(self.allow_yield, self.allow_await) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Keyword((Keyword::Switch, _)) => { SwitchStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Punctuator(Punctuator::OpenBlock) => { BlockStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner) - .map(Node::from) + .map(ast::Statement::from) } TokenKind::Punctuator(Punctuator::Semicolon) => { // parse the EmptyStatement cursor.next(interner).expect("semicolon disappeared"); - Ok(Node::Empty) + Ok(ast::Statement::Empty) } TokenKind::Identifier(_) => { // Labelled Statement check @@ -214,7 +203,7 @@ where self.allow_return, ) .parse(cursor, interner) - .map(Node::from); + .map(ast::Statement::from); } } @@ -268,7 +257,7 @@ impl TokenParser for StatementList where R: Read, { - type Output = node::StatementList; + type Output = ast::statement::StatementList; /// The function parses a `node::StatementList` using the `StatementList`'s /// `break_nodes` to know when to terminate. @@ -280,11 +269,7 @@ where /// /// Note that the last token which causes the parse to finish is not /// consumed. - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("StatementList", "Parsing"); let mut items = Vec::new(); @@ -304,7 +289,7 @@ where while cursor.next_if(Punctuator::Semicolon, interner)?.is_some() {} } - items.sort_by(Node::hoistable_order); + items.sort_by(ast::Statement::hoistable_order); Ok(items.into()) } @@ -347,13 +332,9 @@ impl TokenParser for StatementListItem where R: Read, { - type Output = Node; + type Output = ast::Statement; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("StatementListItem", "Parsing"); let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; @@ -361,14 +342,11 @@ where TokenKind::Keyword(( Keyword::Function | Keyword::Class | Keyword::Const | Keyword::Let, _, - )) => { - Declaration::new(self.allow_yield, self.allow_await, true).parse(cursor, interner) - } + )) => Declaration::new(self.allow_yield, self.allow_await).parse(cursor, interner), TokenKind::Keyword((Keyword::Async, _)) => { match cursor.peek(1, interner)?.map(Token::kind) { Some(TokenKind::Keyword((Keyword::Function, _))) => { - Declaration::new(self.allow_yield, self.allow_await, true) - .parse(cursor, interner) + Declaration::new(self.allow_yield, self.allow_await).parse(cursor, interner) } _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor, interner), @@ -410,13 +388,9 @@ impl TokenParser for ObjectBindingPattern where R: Read, { - type Output = Vec; + type Output = Vec; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ObjectBindingPattern", "Parsing"); cursor.expect( @@ -499,30 +473,18 @@ where self.allow_await, ) .parse(cursor, interner)?; - patterns.push( - BindingPatternTypeObject::BindingPattern { - ident: property_name, - pattern: DeclarationPattern::Object( - DeclarationPatternObject::new( - bindings, None, - ), - ), - default_init: Some(init), - }, - ); + patterns.push(PatternObjectElement::Pattern { + name: property_name, + pattern: bindings.into(), + default_init: Some(init), + }); } _ => { - patterns.push( - BindingPatternTypeObject::BindingPattern { - ident: property_name, - pattern: DeclarationPattern::Object( - DeclarationPatternObject::new( - bindings, None, - ), - ), - default_init: None, - }, - ); + patterns.push(PatternObjectElement::Pattern { + name: property_name, + pattern: bindings.into(), + default_init: None, + }); } } } @@ -544,30 +506,20 @@ where self.allow_await, ) .parse(cursor, interner)?; - patterns.push( - BindingPatternTypeObject::BindingPattern { - ident: property_name, - pattern: DeclarationPattern::Array( - DeclarationPatternArray::new( - bindings, None, - ), - ), - default_init: Some(init), - }, - ); + patterns.push(PatternObjectElement::Pattern { + name: property_name, + pattern: PatternArray::new(bindings.into()) + .into(), + default_init: Some(init), + }); } _ => { - patterns.push( - BindingPatternTypeObject::BindingPattern { - ident: property_name, - pattern: DeclarationPattern::Array( - DeclarationPatternArray::new( - bindings, None, - ), - ), - default_init: None, - }, - ); + patterns.push(PatternObjectElement::Pattern { + name: property_name, + pattern: PatternArray::new(bindings.into()) + .into(), + default_init: None, + }); } } } @@ -589,22 +541,18 @@ where self.allow_await, ) .parse(cursor, interner)?; - patterns.push( - BindingPatternTypeObject::SingleName { - ident, - property_name, - default_init: Some(init), - }, - ); + patterns.push(PatternObjectElement::SingleName { + ident, + name: property_name, + default_init: Some(init), + }); } _ => { - patterns.push( - BindingPatternTypeObject::SingleName { - ident, - property_name, - default_init: None, - }, - ); + patterns.push(PatternObjectElement::SingleName { + ident, + name: property_name, + default_init: None, + }); } } } @@ -624,16 +572,16 @@ where self.allow_await, ) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeObject::SingleName { + patterns.push(PatternObjectElement::SingleName { ident: name, - property_name: name.into(), + name: name.into(), default_init: Some(init), }); } _ => { - patterns.push(BindingPatternTypeObject::SingleName { + patterns.push(PatternObjectElement::SingleName { ident: name, - property_name: name.into(), + name: name.into(), default_init: None, }); } @@ -655,19 +603,19 @@ where if let Some(rest) = rest_property_name { if patterns.is_empty() { - Ok(vec![BindingPatternTypeObject::RestProperty { + Ok(vec![PatternObjectElement::RestProperty { ident: rest, excluded_keys: property_names, }]) } else { - patterns.push(BindingPatternTypeObject::RestProperty { + patterns.push(PatternObjectElement::RestProperty { ident: rest, excluded_keys: property_names, }); Ok(patterns) } } else if patterns.is_empty() { - Ok(vec![BindingPatternTypeObject::Empty]) + Ok(vec![PatternObjectElement::Empty]) } else { Ok(patterns) } @@ -704,13 +652,9 @@ impl TokenParser for ArrayBindingPattern where R: Read, { - type Output = Vec; + type Output = Vec; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ArrayBindingPattern", "Parsing"); cursor.expect( @@ -743,7 +687,7 @@ where interner, )?; if last_elision_or_first { - patterns.push(BindingPatternTypeArray::Elision); + patterns.push(PatternArrayElement::Elision); } else { last_elision_or_first = true; } @@ -765,26 +709,22 @@ where let bindings = ObjectBindingPattern::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::BindingPatternRest { - pattern: DeclarationPattern::Object(DeclarationPatternObject::new( - bindings, None, - )), + patterns.push(PatternArrayElement::PatternRest { + pattern: bindings.into(), }); } TokenKind::Punctuator(Punctuator::OpenBracket) => { let bindings = Self::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::BindingPatternRest { - pattern: DeclarationPattern::Array(DeclarationPatternArray::new( - bindings, None, - )), + patterns.push(PatternArrayElement::PatternRest { + pattern: bindings.into(), }); } _ => { let rest_property_name = BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::SingleNameRest { + patterns.push(PatternArrayElement::SingleNameRest { ident: rest_property_name, }); } @@ -812,18 +752,15 @@ where let default_init = Initializer::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Object(DeclarationPatternObject::new( - bindings, - Some(default_init), - )), + patterns.push(PatternArrayElement::Pattern { + pattern: bindings.into(), + default_init: Some(default_init), }); } _ => { - patterns.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Object(DeclarationPatternObject::new( - bindings, None, - )), + patterns.push(PatternArrayElement::Pattern { + pattern: bindings.into(), + default_init: None, }); } } @@ -843,18 +780,15 @@ where let default_init = Initializer::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Array(DeclarationPatternArray::new( - bindings, - Some(default_init), - )), + patterns.push(PatternArrayElement::Pattern { + pattern: bindings.into(), + default_init: Some(default_init), }); } _ => { - patterns.push(BindingPatternTypeArray::BindingPattern { - pattern: DeclarationPattern::Array(DeclarationPatternArray::new( - bindings, None, - )), + patterns.push(PatternArrayElement::Pattern { + pattern: bindings.into(), + default_init: None, }); } } @@ -877,13 +811,13 @@ where self.allow_await, ) .parse(cursor, interner)?; - patterns.push(BindingPatternTypeArray::SingleName { + patterns.push(PatternArrayElement::SingleName { ident, default_init: Some(default_init), }); } _ => { - patterns.push(BindingPatternTypeArray::SingleName { + patterns.push(PatternArrayElement::SingleName { ident, default_init: None, }); @@ -900,7 +834,7 @@ where interner, )?; if last_elision_or_first { - patterns.push(BindingPatternTypeArray::Elision); + patterns.push(PatternArrayElement::Elision); } else { last_elision_or_first = true; } 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 5cd3f1a46fe..47c0f4ffe48 100644 --- a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/return_stm/mod.rs @@ -1,10 +1,10 @@ use crate::syntax::{ - ast::{node::Return, Keyword, Node, Punctuator}, + ast::{statement::Return, Keyword, Punctuator}, lexer::TokenKind, parser::{ cursor::{Cursor, SemicolonResult}, expression::Expression, - AllowAwait, AllowYield, ParseError, TokenParser, + AllowAwait, AllowYield, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -45,11 +45,7 @@ where { type Output = Return; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ReturnStatement", "Parsing"); cursor.expect((Keyword::Return, false), "return statement", interner)?; @@ -61,7 +57,7 @@ where _ => {} } - return Ok(Return::new::, Option<_>>(None, None)); + return Ok(Return::new(None, None)); } let expr = Expression::new(None, true, self.allow_yield, self.allow_await) @@ -69,6 +65,6 @@ where cursor.expect_semicolon("return statement", interner)?; - Ok(Return::new(expr, None)) + Ok(Return::new(Some(expr), None)) } } diff --git a/boa_engine/src/syntax/parser/statement/switch/mod.rs b/boa_engine/src/syntax/parser/statement/switch/mod.rs index 48f0de0b3aa..6ed53420374 100644 --- a/boa_engine/src/syntax/parser/statement/switch/mod.rs +++ b/boa_engine/src/syntax/parser/statement/switch/mod.rs @@ -2,11 +2,11 @@ mod tests; use crate::syntax::{ - ast::{node, node::Switch, Keyword, Punctuator}, + ast::{statement, statement::Switch, Keyword, Punctuator}, lexer::TokenKind, parser::{ expression::Expression, statement::StatementList, AllowAwait, AllowReturn, AllowYield, - Cursor, ParseError, TokenParser, + Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::{Interner, Sym}; @@ -58,11 +58,7 @@ where { type Output = Switch; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("SwitchStatement", "Parsing"); cursor.expect((Keyword::Switch, false), "switch statement", interner)?; cursor.expect(Punctuator::OpenParen, "switch statement", interner)?; @@ -113,13 +109,9 @@ impl TokenParser for CaseBlock where R: Read, { - type Output = (Box<[node::Case]>, Option); + type Output = (Box<[statement::Case]>, Option); - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { cursor.expect(Punctuator::OpenBlock, "switch case block", interner)?; let mut cases = Vec::new(); @@ -154,7 +146,7 @@ where ) .parse(cursor, interner)?; - cases.push(node::Case::new(cond, statement_list)); + cases.push(statement::Case::new(cond, statement_list)); } TokenKind::Keyword((Keyword::Default, false)) => { if default.is_some() { @@ -197,12 +189,12 @@ where for case in &cases { lexically_declared_names.extend(case.body().lexically_declared_names()); - case.body().var_declared_names_new(&mut var_declared_names); + case.body().var_declared_names(&mut var_declared_names); } if let Some(default_clause) = &default { lexically_declared_names.extend(default_clause.lexically_declared_names()); - default_clause.var_declared_names_new(&mut var_declared_names); + default_clause.var_declared_names(&mut var_declared_names); } let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); diff --git a/boa_engine/src/syntax/parser/statement/switch/tests.rs b/boa_engine/src/syntax/parser/statement/switch/tests.rs index c2c05fe8230..e139e36da79 100644 --- a/boa_engine/src/syntax/parser/statement/switch/tests.rs +++ b/boa_engine/src/syntax/parser/statement/switch/tests.rs @@ -1,17 +1,16 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - Break, Call, Case, Declaration, DeclarationList, GetConstField, Identifier, Node, - Switch, - }, - Const, +use crate::syntax::{ + ast::{ + expression::{access::PropertyAccess, literal::Literal, Call, Identifier}, + statement::{ + declaration::{Declaration, DeclarationList}, + Break, Case, Switch, }, - parser::tests::{check_invalid, check_parser}, + Expression, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::Interner; +use boa_macros::utf16; /// Checks parsing malformed switch with no closeblock. #[test] @@ -161,46 +160,54 @@ fn check_separated_switch() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - a, - Node::from(Const::from(10)), + vec![Declaration::from_identifier( + a.into(), + Some(Literal::from(10).into()), )] .into(), ) .into(), Switch::new( - Identifier::new(a), + Identifier::new(a).into(), vec![ Case::new( - Const::from(5), + Literal::from(5).into(), vec![ - Call::new( - GetConstField::new(Identifier::new(console), log), - vec![Node::from(Const::from(5))], - ) + Expression::from(Call::new( + PropertyAccess::new(Identifier::new(console).into(), log).into(), + vec![Literal::from(5).into()].into(), + )) .into(), Break::new(None).into(), - ], + ] + .into(), ), Case::new( - Const::from(10), + Literal::from(10).into(), vec![ - Call::new( - GetConstField::new(Identifier::new(console), log), - vec![Node::from(Const::from(10))], - ) + Expression::from(Call::new( + PropertyAccess::new(Identifier::new(console).into(), log).into(), + vec![Literal::from(10).into()].into(), + )) .into(), Break::new(None).into(), - ], + ] + .into(), ), - ], - Some(vec![Call::new( - GetConstField::new(Identifier::new(console), log), - vec![Node::from(Const::from( - interner.get_or_intern_static("Default", utf16!("Default")), - ))], - ) - .into()]), + ] + .into(), + Some( + vec![Expression::from(Call::new( + PropertyAccess::new(Identifier::new(console).into(), log).into(), + vec![Literal::from( + interner.get_or_intern_static("Default", utf16!("Default")), + ) + .into()] + .into(), + )) + .into()] + .into(), + ), ) .into(), ], diff --git a/boa_engine/src/syntax/parser/statement/throw/mod.rs b/boa_engine/src/syntax/parser/statement/throw/mod.rs index f7cbc3468c9..c53e8bfcfd0 100644 --- a/boa_engine/src/syntax/parser/statement/throw/mod.rs +++ b/boa_engine/src/syntax/parser/statement/throw/mod.rs @@ -2,9 +2,9 @@ mod tests; use crate::syntax::{ - ast::{node::Throw, Keyword, Punctuator}, + ast::{statement::Throw, Keyword, Punctuator}, lexer::TokenKind, - parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser}, + parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, }; use boa_interner::Interner; use boa_profiler::Profiler; @@ -44,11 +44,7 @@ where { type Output = Throw; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ThrowStatement", "Parsing"); cursor.expect((Keyword::Throw, false), "throw statement", interner)?; diff --git a/boa_engine/src/syntax/parser/statement/throw/tests.rs b/boa_engine/src/syntax/parser/statement/throw/tests.rs index c8de7b98947..56334ced524 100644 --- a/boa_engine/src/syntax/parser/statement/throw/tests.rs +++ b/boa_engine/src/syntax/parser/statement/throw/tests.rs @@ -1,20 +1,18 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{node::Throw, Const}, - parser::tests::check_parser, - }, +use crate::syntax::{ + ast::{expression::literal::Literal, statement::Throw}, + parser::tests::check_parser, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn check_throw_parsing() { let mut interner = Interner::default(); check_parser( "throw 'error';", - vec![Throw::new(Const::from( - interner.get_or_intern_static("error", utf16!("error")), - )) + vec![Throw::new( + Literal::from(interner.get_or_intern_static("error", utf16!("error"))).into(), + ) .into()], interner, ); 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 121fd42990a..6bed1a3e300 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs @@ -1,12 +1,12 @@ use crate::syntax::{ ast::{ - node::{self, Identifier}, + statement::{self, declaration::Binding}, Keyword, Punctuator, }, lexer::TokenKind, parser::{ statement::{block::Block, ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern}, - AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; use boa_interner::Interner; @@ -49,13 +49,9 @@ impl TokenParser for Catch where R: Read, { - type Output = node::Catch; + type Output = statement::Catch; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Catch", "Parsing"); cursor.expect((Keyword::Catch, false), "try statement", interner)?; let position = cursor @@ -75,7 +71,7 @@ where // It is a Syntax Error if BoundNames of CatchParameter contains any duplicate elements. // https://tc39.es/ecma262/#sec-try-statement-static-semantics-early-errors - if let Some(node::Declaration::Pattern(pattern)) = &catch_param { + if let Some(Binding::Pattern(pattern)) = &catch_param { let mut set = FxHashSet::default(); for ident in pattern.idents() { if !set.insert(ident) { @@ -101,7 +97,7 @@ where // https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks let lexically_declared_names = catch_block.lexically_declared_names(); match &catch_param { - Some(node::Declaration::Identifier { ident, .. }) => { + Some(Binding::Identifier(ident)) => { if lexically_declared_names.contains(&(ident.sym(), false)) { return Err(ParseError::general( "catch parameter identifier declared in catch body", @@ -115,9 +111,9 @@ where )); } } - Some(node::Declaration::Pattern(pattern)) => { + Some(Binding::Pattern(pattern)) => { let mut var_declared_names = FxHashSet::default(); - for node in catch_block.items() { + for node in catch_block.statements() { node.var_declared_names(&mut var_declared_names); } for ident in pattern.idents() { @@ -144,7 +140,7 @@ where _ => {} } - let catch_node = node::Catch::new::<_, node::Declaration, _>(catch_param, catch_block); + let catch_node = statement::Catch::new(catch_param, catch_block); Ok(catch_node) } } @@ -181,13 +177,9 @@ impl TokenParser for CatchParameter where R: Read, { - type Output = node::Declaration; + type Output = Binding; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; match token.kind() { @@ -195,18 +187,17 @@ where let pat = ObjectBindingPattern::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - Ok(node::Declaration::new_with_object_pattern(pat, None)) + Ok(Binding::Pattern(pat.into())) } TokenKind::Punctuator(Punctuator::OpenBracket) => { let pat = ArrayBindingPattern::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - Ok(node::Declaration::new_with_array_pattern(pat, None)) + Ok(Binding::Pattern(pat.into())) } TokenKind::Identifier(_) => { let ident = BindingIdentifier::new(self.allow_yield, self.allow_await) - .parse(cursor, interner) - .map(Identifier::new)?; - Ok(node::Declaration::new_with_identifier(ident, None)) + .parse(cursor, interner)?; + Ok(Binding::Identifier(ident.into())) } _ => Err(ParseError::unexpected( token.to_string(interner), 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 ca5e4387531..3a6262e0c78 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/finally.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/finally.rs @@ -1,7 +1,7 @@ use crate::syntax::{ - ast::{node, Keyword}, + ast::{statement, Keyword}, parser::{ - statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, + statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, TokenParser, }, }; @@ -44,13 +44,9 @@ impl TokenParser for Finally where R: Read, { - type Output = node::Finally; + type Output = statement::Finally; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Finally", "Parsing"); cursor.expect((Keyword::Finally, false), "try statement", interner)?; Ok( 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 73cba95889e..73aa05a93da 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/mod.rs @@ -7,9 +7,9 @@ mod tests; use self::{catch::Catch, finally::Finally}; use super::block::Block; use crate::syntax::{ - ast::{node::Try, Keyword}, + ast::{statement::Try, Keyword}, lexer::TokenKind, - parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, + parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; use boa_interner::Interner; use boa_profiler::Profiler; @@ -52,11 +52,7 @@ where { type Output = Try; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("TryStatement", "Parsing"); // TRY cursor.expect((Keyword::Try, false), "try statement", interner)?; 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 23587b17375..aff8c6d7d1e 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs @@ -1,18 +1,17 @@ -use crate::{ - string::utf16, - syntax::{ - ast::{ - node::{ - declaration::{BindingPatternTypeArray, BindingPatternTypeObject}, - object::PropertyName, - Block, Catch, Declaration, DeclarationList, Finally, Identifier, Try, - }, - Const, +use crate::syntax::{ + ast::{ + expression::{literal::Literal, Identifier}, + pattern::{Pattern, PatternArrayElement, PatternObjectElement}, + property::PropertyName, + statement::{ + declaration::{Declaration, DeclarationList}, + Block, Catch, Finally, Try, }, - parser::tests::{check_invalid, check_parser}, }, + parser::tests::{check_invalid, check_parser}, }; use boa_interner::Interner; +use boa_macros::utf16; #[test] fn check_inline_with_empty_try_catch() { @@ -20,13 +19,10 @@ fn check_inline_with_empty_try_catch() { check_parser( "try { } catch(e) {}", vec![Try::new( - vec![], + Block::default(), Some(Catch::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("e", utf16!("e")), - None, - ), - vec![], + Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), + Block::default(), )), None, ) @@ -42,19 +38,17 @@ fn check_inline_with_var_decl_inside_try() { "try { var x = 1; } catch(e) {}", vec![Try::new( vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Const::from(1).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(Literal::from(1).into()), )] .into(), ) - .into()], + .into()] + .into(), Some(Catch::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("e", utf16!("e")), - None, - ), - vec![], + Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), + Block::default(), )), None, ) @@ -70,26 +64,25 @@ fn check_inline_with_var_decl_inside_catch() { "try { var x = 1; } catch(e) { var x = 1; }", vec![Try::new( vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Const::from(1).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(Literal::from(1).into()), )] .into(), ) - .into()], + .into()] + .into(), Some(Catch::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("e", utf16!("e")), - None, - ), + Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Const::from(1).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(Literal::from(1).into()), )] .into(), ) - .into()], + .into()] + .into(), )), None, ) @@ -104,15 +97,12 @@ fn check_inline_with_empty_try_catch_finally() { check_parser( "try {} catch(e) {} finally {}", vec![Try::new( - vec![], + Block::default(), Some(Catch::new( - Declaration::new_with_identifier( - interner.get_or_intern_static("e", utf16!("e")), - None, - ), - vec![], + Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()), + Block::default(), )), - Some(Finally::from(vec![])), + Some(Finally::from(Block::default())), ) .into()], interner, @@ -123,7 +113,12 @@ fn check_inline_with_empty_try_catch_finally() { fn check_inline_with_empty_try_finally() { check_parser( "try {} finally {}", - vec![Try::new(vec![], None, Some(Finally::from(vec![]))).into()], + vec![Try::new( + Block::default(), + None, + Some(Finally::from(Block::default())), + ) + .into()], Interner::default(), ); } @@ -134,12 +129,12 @@ fn check_inline_with_empty_try_var_decl_in_finally() { check_parser( "try {} finally { var x = 1; }", vec![Try::new( - vec![], + Block::default(), None, Some(Finally::from(vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Const::from(1).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(Literal::from(1).into()), )] .into(), ) @@ -156,17 +151,18 @@ fn check_inline_empty_try_paramless_catch() { check_parser( "try {} catch { var x = 1; }", vec![Try::new( - Block::from(vec![]), - Some(Catch::new::<_, Declaration, _>( + Block::default(), + Some(Catch::new( None, vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Const::from(1).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(Literal::from(1).into()), )] .into(), ) - .into()], + .into()] + .into(), )), None, ) @@ -182,26 +178,26 @@ fn check_inline_with_binding_pattern_object() { check_parser( "try {} catch ({ a, b: c }) {}", vec![Try::new( - Block::from(vec![]), - Some(Catch::new::<_, Declaration, _>( - Some(Declaration::new_with_object_pattern( - vec![ - BindingPatternTypeObject::SingleName { + Block::default(), + Some(Catch::new( + Some( + Pattern::from(vec![ + PatternObjectElement::SingleName { ident: a, - property_name: PropertyName::Literal(a), + name: PropertyName::Literal(a), default_init: None, }, - BindingPatternTypeObject::SingleName { + PatternObjectElement::SingleName { ident: interner.get_or_intern_static("c", utf16!("c")), - property_name: PropertyName::Literal( + name: PropertyName::Literal( interner.get_or_intern_static("b", utf16!("b")), ), default_init: None, }, - ], - None, - )), - vec![], + ]) + .into(), + ), + Block::default(), )), None, ) @@ -217,21 +213,21 @@ fn check_inline_with_binding_pattern_array() { "try {} catch ([a, b]) {}", vec![Try::new( Block::from(vec![]), - Some(Catch::new::<_, Declaration, _>( - Some(Declaration::new_with_array_pattern( - vec![ - BindingPatternTypeArray::SingleName { + Some(Catch::new( + Some( + Pattern::from(vec![ + PatternArrayElement::SingleName { ident: interner.get_or_intern_static("a", utf16!("a")), default_init: None, }, - BindingPatternTypeArray::SingleName { + PatternArrayElement::SingleName { ident: interner.get_or_intern_static("b", utf16!("b")), default_init: None, }, - ], - None, - )), - vec![], + ]) + .into(), + ), + Block::default(), )), None, ) @@ -247,19 +243,19 @@ fn check_catch_with_var_redeclaration() { "try {} catch(e) { var e = 'oh' }", vec![Try::new( Block::from(vec![]), - Some(Catch::new::<_, Declaration, _>( - Some(Declaration::new_with_identifier( - Identifier::new(interner.get_or_intern_static("e", utf16!("e"))), - None, - )), + Some(Catch::new( + Some(Identifier::new(interner.get_or_intern_static("e", utf16!("e"))).into()), vec![DeclarationList::Var( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("e", utf16!("e")), - Some(Const::from(interner.get_or_intern_static("oh", utf16!("oh"))).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("e", utf16!("e")).into(), + Some( + Literal::from(interner.get_or_intern_static("oh", utf16!("oh"))).into(), + ), )] .into(), ) - .into()], + .into()] + .into(), )), None, ) diff --git a/boa_engine/src/syntax/parser/statement/variable/mod.rs b/boa_engine/src/syntax/parser/statement/variable/mod.rs index b94acf33a0b..8f9d14846ae 100644 --- a/boa_engine/src/syntax/parser/statement/variable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/variable/mod.rs @@ -2,15 +2,18 @@ use crate::syntax::{ ast::{ - node::{Declaration, DeclarationList}, + statement::declaration::{Declaration, DeclarationList}, Keyword, Punctuator, }, lexer::TokenKind, - parser::statement::{ArrayBindingPattern, ObjectBindingPattern}, parser::{ cursor::Cursor, expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield, ParseError, TokenParser, }, + parser::{ + statement::{ArrayBindingPattern, ObjectBindingPattern}, + ParseResult, + }, }; use boa_interner::Interner; use boa_profiler::Profiler; @@ -52,11 +55,7 @@ where { type Output = DeclarationList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("VariableStatement", "Parsing"); cursor.expect((Keyword::Var, false), "variable statement", interner)?; @@ -110,11 +109,7 @@ where { type Output = DeclarationList; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let mut list = Vec::new(); loop { @@ -167,11 +162,7 @@ where { type Output = Declaration; - fn parse( - self, - cursor: &mut Cursor, - interner: &mut Interner, - ) -> Result { + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let peek_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; match peek_token.kind() { @@ -197,7 +188,7 @@ where None }; - Ok(Declaration::new_with_object_pattern(bindings, init)) + Ok(Declaration::from_pattern(bindings.into(), init)) } TokenKind::Punctuator(Punctuator::OpenBracket) => { let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await) @@ -221,7 +212,7 @@ where None }; - Ok(Declaration::new_with_array_pattern(bindings, init)) + Ok(Declaration::from_pattern(bindings.into(), init)) } _ => { let ident = BindingIdentifier::new(self.allow_yield, self.allow_await) @@ -239,7 +230,7 @@ where } else { None }; - Ok(Declaration::new_with_identifier(ident, init)) + Ok(Declaration::from_identifier(ident.into(), init)) } } } diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests.rs index 6a07f3a47d8..7daad8c3321 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/boa_engine/src/syntax/parser/tests.rs @@ -5,14 +5,26 @@ use crate::{ context::ContextBuilder, string::utf16, syntax::ast::{ - node::{ - field::GetConstField, object::PropertyDefinition, ArrowFunctionDecl, Assign, BinOp, - Call, Declaration, DeclarationList, FormalParameter, FormalParameterList, - FormalParameterListFlags, FunctionDecl, Identifier, If, New, Node, Object, Return, - StatementList, UnaryOp, + expression::{ + access::PropertyAccess, + literal::{Literal, ObjectLiteral}, + operator::{ + assign::op::AssignOp, + binary::op::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp}, + unary::op::UnaryOp, + Assign, Binary, Unary, + }, + Call, Identifier, New, + }, + function::{ + ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function, + }, + property::PropertyDefinition, + statement::{ + declaration::{Declaration, DeclarationList}, + If, Return, StatementList, }, - op::{self, CompOp, LogOp, NumOp}, - Const, + Expression, Statement, }, Context, }; @@ -23,7 +35,7 @@ use boa_interner::Interner; #[track_caller] pub(super) fn check_parser(js: &str, expr: L, interner: Interner) where - L: Into>, + L: Into>, { let mut context = ContextBuilder::default().interner(interner).build(); assert_eq!( @@ -47,16 +59,19 @@ fn check_construct_call_precedence() { let mut interner = Interner::default(); check_parser( "new Date().getTime()", - vec![Node::from(Call::new( - GetConstField::new( + vec![Expression::from(Call::new( + PropertyAccess::new( New::from(Call::new( - Identifier::new(interner.get_or_intern_static("Date", utf16!("Date"))), - vec![], - )), + Identifier::new(interner.get_or_intern_static("Date", utf16!("Date"))).into(), + Box::default(), + )) + .into(), interner.get_or_intern_static("getTime", utf16!("getTime")), - ), - vec![], - ))], + ) + .into(), + Box::default(), + )) + .into()], interner, ); } @@ -66,14 +81,16 @@ fn assign_operator_precedence() { let mut interner = Interner::default(); check_parser( "a = a + 1", - vec![Assign::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - BinOp::new( - NumOp::Add, - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(1), - ), - ) + vec![Expression::from(Assign::new( + AssignOp::Assign, + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Binary::new( + ArithmeticOp::Add.into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(1).into(), + ) + .into(), + )) .into()], interner, ); @@ -91,21 +108,25 @@ fn hoisting() { function hello() { return 10 }", vec![ - FunctionDecl::new( - hello, + Function::new( + Some(hello), FormalParameterList::default(), - vec![Return::new(Const::from(10), None).into()], + vec![Return::new(Some(Literal::from(10).into()), None).into()].into(), ) .into(), DeclarationList::Var( - vec![Declaration::new_with_identifier( - a, - Node::from(Call::new(Identifier::new(hello), vec![])), + vec![Declaration::from_identifier( + a.into(), + Some(Call::new(Identifier::new(hello).into(), Box::default()).into()), )] .into(), ) .into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), ], interner, ); @@ -119,9 +140,18 @@ fn hoisting() { var a;", vec![ - Assign::new(Identifier::new(a), Const::from(10)).into(), - UnaryOp::new(op::UnaryOp::IncrementPost, Identifier::new(a)).into(), - DeclarationList::Var(vec![Declaration::new_with_identifier(a, None)].into()).into(), + Expression::from(Assign::new( + AssignOp::Assign, + Identifier::new(a).into(), + Literal::from(10).into(), + )) + .into(), + Expression::from(Unary::new( + UnaryOp::IncrementPost, + Identifier::new(a).into(), + )) + .into(), + DeclarationList::Var(vec![Declaration::from_identifier(a.into(), None)].into()).into(), ], interner, ); @@ -134,19 +164,21 @@ fn ambigous_regex_divide_expression() { let mut interner = Interner::default(); check_parser( s, - vec![BinOp::new( - CompOp::StrictEqual, - BinOp::new( - NumOp::Div, - Const::Int(1), - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - ), - BinOp::new( - NumOp::Div, - Const::Int(1), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ), - ) + vec![Expression::from(Binary::new( + RelationalOp::StrictEqual.into(), + Binary::new( + ArithmeticOp::Div.into(), + Literal::Int(1).into(), + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + ) + .into(), + Binary::new( + ArithmeticOp::Div.into(), + Literal::Int(1).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), + )) .into()], interner, ); @@ -160,19 +192,31 @@ fn two_divisions_in_expression() { let a = interner.get_or_intern_static("a", utf16!("a")); check_parser( s, - vec![BinOp::new( - LogOp::Or, - BinOp::new(CompOp::StrictNotEqual, Identifier::new(a), Const::Int(0)), - BinOp::new( - CompOp::StrictEqual, - BinOp::new(NumOp::Div, Const::Int(1), Identifier::new(a)), - BinOp::new( - NumOp::Div, - Const::Int(1), - Identifier::new(interner.get_or_intern_static("b", utf16!("b"))), - ), - ), - ) + vec![Expression::from(Binary::new( + LogicalOp::Or.into(), + Binary::new( + RelationalOp::StrictNotEqual.into(), + Identifier::new(a).into(), + Literal::Int(0).into(), + ) + .into(), + Binary::new( + RelationalOp::StrictEqual.into(), + Binary::new( + ArithmeticOp::Div.into(), + Literal::Int(1).into(), + Identifier::new(a).into(), + ) + .into(), + Binary::new( + ArithmeticOp::Div.into(), + Literal::Int(1).into(), + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), + ) + .into(), + ) + .into(), + )) .into()], interner, ); @@ -190,17 +234,17 @@ fn comment_semi_colon_insertion() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::Int(10).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::Int(10).into()), )] .into(), ) .into(), DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), - Some(Const::Int(20).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), + Some(Literal::Int(20).into()), )] .into(), ) @@ -224,17 +268,17 @@ fn multiline_comment_semi_colon_insertion() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::Int(10).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::Int(10).into()), )] .into(), ) .into(), DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), - Some(Const::Int(20).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), + Some(Literal::Int(20).into()), )] .into(), ) @@ -255,17 +299,17 @@ fn multiline_comment_no_lineterminator() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::Int(10).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::Int(10).into()), )] .into(), ) .into(), DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("b", utf16!("b")), - Some(Const::Int(20).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("b", utf16!("b")).into(), + Some(Literal::Int(20).into()), )] .into(), ) @@ -289,17 +333,18 @@ fn assignment_line_terminator() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("a", utf16!("a")), - Some(Const::Int(3).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("a", utf16!("a")).into(), + Some(Literal::Int(3).into()), )] .into(), ) .into(), - Assign::new( - Identifier::new(interner.get_or_intern_static("a", utf16!("a"))), - Const::from(5), - ) + Expression::from(Assign::new( + AssignOp::Assign, + Identifier::new(interner.get_or_intern_static("a", utf16!("a"))).into(), + Literal::from(5).into(), + )) .into(), ], interner, @@ -324,14 +369,19 @@ fn assignment_multiline_terminator() { s, vec![ DeclarationList::Let( - vec![Declaration::new_with_identifier( - a, - Some(Const::Int(3).into()), + vec![Declaration::from_identifier( + a.into(), + Some(Literal::Int(3).into()), )] .into(), ) .into(), - Assign::new(Identifier::new(a), Const::from(5)).into(), + Expression::from(Assign::new( + AssignOp::Assign, + Identifier::new(a).into(), + Literal::from(5).into(), + )) + .into(), ], interner, ); @@ -344,7 +394,10 @@ fn bracketed_expr() { let mut interner = Interner::default(); check_parser( s, - vec![Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into()], + vec![Expression::from(Identifier::new( + interner.get_or_intern_static("b", utf16!("b")), + )) + .into()], interner, ); } @@ -357,11 +410,11 @@ fn increment_in_comma_op() { let b = interner.get_or_intern_static("b", utf16!("b")); check_parser( s, - vec![BinOp::new::<_, Node, Node>( - op::BinOp::Comma, - UnaryOp::new::(op::UnaryOp::IncrementPost, Identifier::new(b).into()).into(), + vec![Expression::from(Binary::new( + BinaryOp::Comma, + Unary::new(UnaryOp::IncrementPost, Identifier::new(b).into()).into(), Identifier::new(b).into(), - ) + )) .into()], interner, ); @@ -379,21 +432,21 @@ fn spread_in_object() { let mut interner = Interner::default(); let object_properties = vec![ - PropertyDefinition::property( - interner.get_or_intern_static("a", utf16!("a")), - Const::from(1), + PropertyDefinition::Property( + interner.get_or_intern_static("a", utf16!("a")).into(), + Literal::from(1).into(), + ), + PropertyDefinition::SpreadObject( + Identifier::new(interner.get_or_intern_static("b", utf16!("b"))).into(), ), - PropertyDefinition::spread_object(Identifier::new( - interner.get_or_intern_static("b", utf16!("b")), - )), ]; check_parser( s, vec![DeclarationList::Let( - vec![Declaration::new_with_identifier( - interner.get_or_intern_static("x", utf16!("x")), - Some(Object::from(object_properties).into()), + vec![Declaration::from_identifier( + interner.get_or_intern_static("x", utf16!("x")).into(), + Some(ObjectLiteral::from(object_properties).into()), )] .into(), ) @@ -414,19 +467,19 @@ fn spread_in_arrow_function() { let b = interner.get_or_intern_static("b", utf16!("b")); check_parser( s, - vec![ArrowFunctionDecl::new( + vec![Expression::from(ArrowFunction::new( None, FormalParameterList { parameters: Box::new([FormalParameter::new( - Declaration::new_with_identifier(b, None), + Declaration::from_identifier(b.into(), None), true, )]), flags: FormalParameterListFlags::empty() .union(FormalParameterListFlags::HAS_REST_PARAMETER), length: 0, }, - vec![Identifier::from(b).into()], - ) + vec![Expression::from(Identifier::from(b)).into()].into(), + )) .into()], interner, ); @@ -442,20 +495,16 @@ fn empty_statement() { if(a) ; ", vec![ - Node::Empty, + Statement::Empty, DeclarationList::Var( - vec![Declaration::new_with_identifier( - a, - Node::from(Const::from(10)), + vec![Declaration::from_identifier( + a.into(), + Some(Literal::from(10).into()), )] .into(), ) .into(), - Node::If(If::new::<_, _, Node, _>( - Identifier::new(a), - Node::Empty, - None, - )), + If::new(Identifier::new(a).into(), Statement::Empty, None).into(), ], interner, ); diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 38e8707f650..1e9a49b182e 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -18,7 +18,7 @@ use crate::{ internal_methods::get_prototype_from_constructor, JsObject, ObjectData, PrivateElement, }, property::PropertyDescriptor, - syntax::ast::node::FormalParameterList, + syntax::ast::function::FormalParameterList, vm::call_frame::GeneratorResumeKind, vm::{call_frame::FinallyReturn, CallFrame, Opcode}, Context, JsResult, JsString, JsValue, diff --git a/boa_engine/src/vm/opcode.rs b/boa_engine/src/vm/opcode.rs index dde32d9cb80..ee5982b8965 100644 --- a/boa_engine/src/vm/opcode.rs +++ b/boa_engine/src/vm/opcode.rs @@ -373,7 +373,7 @@ pub enum Opcode { /// /// Operands: exit: `u32` /// - /// Stack: lhs, rhs **=>** (lhs && rhs) + /// Stack: lhs, rhs **=>** (lhs ?? rhs) Coalesce, /// Unary `typeof` operator. From 17235e4b96fc65fea74d38fd2f605fe3dfcde16d Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Fri, 30 Sep 2022 12:02:38 -0500 Subject: [PATCH 2/8] Fix var declared names fetch --- boa_engine/src/builtins/eval/mod.rs | 6 ++---- boa_engine/src/bytecompiler/mod.rs | 6 +++--- boa_engine/src/syntax/ast/statement/mod.rs | 5 +++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index d1da15801f5..11fa31afb2f 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -66,9 +66,7 @@ impl Eval { context: &mut Context, ) -> Result { // 1. Assert: If direct is false, then strictCaller is also false. - if !direct { - debug_assert!(!strict); - } + debug_assert!(direct || !strict); // 2. If Type(x) is not String, return x. let x = if let Some(x) = x.as_string() { @@ -109,7 +107,7 @@ impl Eval { .has_lex_binding_until_function_environment(&vars) { let name = context.interner().resolve_expect(name); - let msg = format!("variable declaration {name} in eval function already exists as lexically declaration"); + let msg = format!("variable declaration `{name}` in eval function already exists as lexically declaration"); return context.throw_syntax_error(msg); } diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 50d94064714..ddbea04a00d 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -2351,11 +2351,11 @@ impl<'b> ByteCompiler<'b> { // SingleNameBinding : BindingIdentifier Initializer[opt] SingleName { ident, - name: property_name, + name, default_init, } => { self.emit_opcode(Opcode::Dup); - match property_name { + match name { PropertyName::Literal(name) => { let index = self.get_or_insert_name(*name); self.emit(Opcode::GetPropertyByName, &[index]); @@ -2379,7 +2379,7 @@ impl<'b> ByteCompiler<'b> { } self.emit_binding(def, *ident); - if rest_exits && property_name.computed().is_some() { + if rest_exits && name.computed().is_some() { self.emit_opcode(Opcode::Swap); additional_excluded_keys_count += 1; } diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs index 02d8a6d187a..768b6242f35 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/boa_engine/src/syntax/ast/statement/mod.rs @@ -181,6 +181,11 @@ impl Statement { pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { match self { + Statement::DeclarationList(DeclarationList::Var(list)) => { + for decl in &**list { + vars.extend(decl.idents()); + } + } Statement::Block(block) => { for node in block.statements() { node.var_declared_names(vars); From 790810aef8b7812e9c6ff9d7031623d3dc3a56a6 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Fri, 30 Sep 2022 18:05:55 -0500 Subject: [PATCH 3/8] Remove panics on unimplemented features --- boa_engine/src/bytecompiler/mod.rs | 3 +- .../ast/statement/statement_list/mod.rs | 24 +++- boa_engine/src/syntax/parser/mod.rs | 104 +++++++++++------- .../statement/iteration/for_statement.rs | 9 +- 4 files changed, 94 insertions(+), 46 deletions(-) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index ddbea04a00d..1985db4208f 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -734,7 +734,8 @@ impl<'b> ByteCompiler<'b> { let index = self.get_or_insert_name(access.field()); self.emit(Opcode::AssignPrivateField, &[index]); } - Access::SuperProperty { field: _field } => todo!("access_set `super`"), + // TODO: access_set `super` + Access::SuperProperty { field: _field } => {} Access::This => todo!("access_set `this`"), } Ok(()) diff --git a/boa_engine/src/syntax/ast/statement/statement_list/mod.rs b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs index 6b77109adf5..2cb2bc1ca81 100644 --- a/boa_engine/src/syntax/ast/statement/statement_list/mod.rs +++ b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs @@ -1,6 +1,6 @@ //! Statement list node. -use crate::syntax::ast::statement::Statement; +use crate::syntax::ast::{statement::Statement, ContainsSymbol}; use boa_interner::{Interner, Sym, ToInternedString}; use rustc_hash::FxHashSet; @@ -179,6 +179,28 @@ impl StatementList { names } + + /// Returns `true` if the node contains the given token. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.statements.iter().any(|stmt| stmt.contains(symbol)) + } + + /// Returns true if the node contains a identifier reference named 'arguments'. + /// + /// More information: + /// - [ECMAScript specification][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.statements.iter().any(Statement::contains_arguments) + } } impl From for StatementList diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs index 9d2313f15c9..adce0c94df5 100644 --- a/boa_engine/src/syntax/parser/mod.rs +++ b/boa_engine/src/syntax/parser/mod.rs @@ -135,6 +135,11 @@ impl Parser { Script::new(false).parse(&mut self.cursor, context) } + /// [`19.2.1.1 PerformEval ( x, strictCaller, direct )`][spec] + /// + /// Parses the source text input of an `eval` call. + /// + /// [spec]: https://tc39.es/ecma262/#sec-performeval pub(crate) fn parse_eval( &mut self, direct: bool, @@ -143,67 +148,84 @@ impl Parser { where R: Read, { - let (in_function, in_method, in_derived_constructor) = if let Some(function_env) = context + #[derive(Debug, Default)] + #[allow(clippy::struct_excessive_bools)] + struct Flags { + in_function: bool, + in_method: bool, + in_derived_constructor: bool, + in_class_field_initializer: bool, + } + // 5. Perform ? HostEnsureCanCompileStrings(evalRealm). + // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection: + // a. Let script be ParseText(StringToCodePoints(x), Script). + // b. If script is a List of errors, throw a SyntaxError exception. + // c. If script Contains ScriptBody is false, return undefined. + // d. Let body be the ScriptBody of script. + let body = Script::new(direct).parse(&mut self.cursor, context)?; + + // 6. Let inFunction be false. + // 7. Let inMethod be false. + // 8. Let inDerivedConstructor be false. + // 9. Let inClassFieldInitializer be false. + // a. Let thisEnvRec be GetThisEnvironment(). + let flags = match context .realm .environments .get_this_environment() .as_function_slots() { - let function_env_borrow = function_env.borrow(); - let has_super_binding = function_env_borrow.has_super_binding(); - let function_object = function_env_borrow.function_object().borrow(); - ( - true, - has_super_binding, - function_object - .as_function() - .expect("must be function object") - .is_derived_constructor(), - ) - } else { - (false, false, false) - }; - - let statement_list = Script::new(direct).parse(&mut self.cursor, context)?; - - let mut contains_super_property = false; - let mut contains_super_call = false; - let mut contains_new_target = false; - if direct { - for node in statement_list.statements() { - if !contains_super_property && node.contains(ContainsSymbol::SuperProperty) { - contains_super_property = true; - } - - if !contains_super_call && node.contains(ContainsSymbol::SuperCall) { - contains_super_call = true; - } - - if !contains_new_target && node.contains(ContainsSymbol::NewTarget) { - contains_new_target = true; + // 10. If direct is true, then + // b. If thisEnvRec is a Function Environment Record, then + Some(function_env) if direct => { + let function_env = function_env.borrow(); + // i. Let F be thisEnvRec.[[FunctionObject]]. + let function_object = function_env.function_object().borrow(); + Flags { + // ii. Set inFunction to true. + in_function: true, + // iii. Set inMethod to thisEnvRec.HasSuperBinding(). + in_method: function_env.has_super_binding(), + // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true. + in_derived_constructor: function_object + .as_function() + .expect("must be function object") + .is_derived_constructor(), + // TODO: + // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]]. + // vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true. + in_class_field_initializer: false, } } - } + _ => Flags::default(), + }; - if !in_function && contains_new_target { + if !flags.in_function && body.contains(ContainsSymbol::NewTarget) { return Err(ParseError::general( - "invalid new.target usage", + "invalid `new.target` expression inside eval", Position::new(1, 1), )); } - if !in_method && contains_super_property { + if !flags.in_method && body.contains(ContainsSymbol::SuperProperty) { return Err(ParseError::general( - "invalid super usage", + "invalid `super` reference inside eval", Position::new(1, 1), )); } - if !in_derived_constructor && contains_super_call { + if !flags.in_derived_constructor && body.contains(ContainsSymbol::SuperCall) { return Err(ParseError::general( - "invalid super usage", + "invalid `super` call inside eval", Position::new(1, 1), )); } - Ok(statement_list) + if !flags.in_class_field_initializer && body.contains_arguments() { + return Err(ParseError::general( + "invalid `arguments` reference inside eval", + Position::new(1, 1), + )); + } + + Ok(body) } /// Parse the full input as an [ECMAScript `FunctionBody`][spec] into the boa AST representation. 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 0080687583b..a39625cec7a 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs @@ -348,9 +348,12 @@ fn initializer_to_iterable_loop_initializer( ast::Expression::Identifier(ident) => Ok(IterableLoopInitializer::Identifier(ident)), // TODO: implement member initializers - ast::Expression::PropertyAccess(_) => todo!(), - ast::Expression::SuperPropertyAccess(_) => todo!(), - ast::Expression::PrivatePropertyAccess(_) => todo!(), + ast::Expression::PropertyAccess(_) + | ast::Expression::SuperPropertyAccess(_) + | ast::Expression::PrivatePropertyAccess(_) => Err(ParseError::Unimplemented { + message: "using a property access as iterable loop initializer is not implemented", + position, + }), _ => Err(ParseError::lex(LexError::Syntax( "invalid variable for iterable loop".into(), position, From 2ee1f17676beacb93dc8d3eacdeb2a8fae644012 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sat, 1 Oct 2022 00:28:00 -0500 Subject: [PATCH 4/8] Cleanup `contains` function --- boa_engine/src/builtins/eval/mod.rs | 2 +- boa_engine/src/builtins/function/mod.rs | 2 +- boa_engine/src/bytecompiler/function.rs | 26 +- boa_engine/src/bytecompiler/mod.rs | 206 ++++----- boa_engine/src/environments/compile.rs | 45 +- boa_engine/src/environments/runtime.rs | 31 +- .../src/syntax/ast/expression/access.rs | 47 +- boa_engine/src/syntax/ast/expression/await.rs | 12 + boa_engine/src/syntax/ast/expression/call.rs | 22 +- .../src/syntax/ast/expression/identifier.rs | 15 +- .../syntax/ast/expression/literal/array.rs | 15 +- .../ast/expression/literal/object/mod.rs | 19 +- .../syntax/ast/expression/literal/template.rs | 21 +- boa_engine/src/syntax/ast/expression/mod.rs | 431 +++--------------- boa_engine/src/syntax/ast/expression/new.rs | 12 +- .../ast/expression/operator/assign/mod.rs | 65 ++- .../ast/expression/operator/binary/mod.rs | 12 +- .../ast/expression/operator/conditional.rs | 16 +- .../ast/expression/operator/unary/mod.rs | 12 +- .../src/syntax/ast/expression/spread.rs | 12 + .../syntax/ast/expression/tagged_template.rs | 11 + boa_engine/src/syntax/ast/expression/yield.rs | 12 + .../src/syntax/ast/function/arrow_function.rs | 33 +- .../src/syntax/ast/function/async_function.rs | 11 +- .../syntax/ast/function/async_generator.rs | 12 +- boa_engine/src/syntax/ast/function/class.rs | 90 ++-- .../src/syntax/ast/function/generator.rs | 12 +- boa_engine/src/syntax/ast/function/mod.rs | 12 +- .../src/syntax/ast/function/parameters.rs | 31 +- boa_engine/src/syntax/ast/mod.rs | 4 + boa_engine/src/syntax/ast/pattern.rs | 56 ++- boa_engine/src/syntax/ast/property.rs | 71 ++- boa_engine/src/syntax/ast/statement/block.rs | 14 +- .../src/syntax/ast/statement/declaration.rs | 19 +- boa_engine/src/syntax/ast/statement/if.rs | 16 +- .../syntax/ast/statement/iteration/break.rs | 5 + .../ast/statement/iteration/continue.rs | 5 + .../ast/statement/iteration/do_while_loop.rs | 14 +- .../ast/statement/iteration/for_in_loop.rs | 14 + .../ast/statement/iteration/for_loop.rs | 37 +- .../ast/statement/iteration/for_of_loop.rs | 14 + .../src/syntax/ast/statement/iteration/mod.rs | 18 +- .../ast/statement/iteration/while_loop.rs | 13 +- boa_engine/src/syntax/ast/statement/mod.rs | 148 ++---- boa_engine/src/syntax/ast/statement/return.rs | 22 +- .../ast/statement/statement_list/mod.rs | 30 +- .../src/syntax/ast/statement/switch/mod.rs | 21 +- boa_engine/src/syntax/ast/statement/throw.rs | 12 +- .../src/syntax/ast/statement/try/mod.rs | 16 +- .../expression/assignment/arrow_function.rs | 7 +- .../expression/assignment/conditional.rs | 11 +- .../expression/assignment/exponentiation.rs | 11 +- .../parser/expression/assignment/mod.rs | 13 +- .../syntax/parser/expression/identifiers.rs | 16 +- .../expression/left_hand_side/member.rs | 6 +- .../parser/expression/left_hand_side/mod.rs | 11 +- .../src/syntax/parser/expression/mod.rs | 53 ++- .../primary/async_function_expression/mod.rs | 9 +- .../async_function_expression/tests.rs | 6 +- .../primary/async_generator_expression/mod.rs | 9 +- .../async_generator_expression/tests.rs | 6 +- .../primary/class_expression/mod.rs | 8 +- .../primary/function_expression/mod.rs | 9 +- .../primary/function_expression/tests.rs | 8 +- .../primary/generator_expression/mod.rs | 9 +- .../primary/generator_expression/tests.rs | 4 +- .../syntax/parser/expression/primary/mod.rs | 16 +- .../primary/object_initializer/mod.rs | 26 +- .../primary/object_initializer/tests.rs | 22 +- .../src/syntax/parser/expression/unary.rs | 11 +- .../src/syntax/parser/expression/update.rs | 9 +- boa_engine/src/syntax/parser/function/mod.rs | 6 +- .../src/syntax/parser/function/tests.rs | 28 +- boa_engine/src/syntax/parser/mod.rs | 10 +- .../src/syntax/parser/statement/block/mod.rs | 6 +- .../syntax/parser/statement/block/tests.rs | 4 +- .../syntax/parser/statement/break_stm/mod.rs | 5 +- .../parser/statement/continue_stm/mod.rs | 5 +- .../hoistable/async_function_decl/tests.rs | 18 +- .../hoistable/async_generator_decl/tests.rs | 2 +- .../declaration/hoistable/class_decl/mod.rs | 33 +- .../declaration/hoistable/class_decl/tests.rs | 6 +- .../hoistable/function_decl/tests.rs | 12 +- .../hoistable/generator_decl/tests.rs | 2 +- .../statement/declaration/hoistable/mod.rs | 7 +- .../parser/statement/declaration/lexical.rs | 6 +- .../parser/statement/labelled_stm/mod.rs | 5 +- boa_engine/src/syntax/parser/statement/mod.rs | 6 +- .../src/syntax/parser/statement/switch/mod.rs | 6 +- .../syntax/parser/statement/try_stm/catch.rs | 6 +- .../syntax/parser/statement/try_stm/tests.rs | 8 +- .../syntax/parser/statement/variable/mod.rs | 2 +- boa_engine/src/syntax/parser/tests.rs | 6 +- boa_engine/src/vm/code_block.rs | 14 +- boa_engine/src/vm/mod.rs | 57 +-- 95 files changed, 1285 insertions(+), 1051 deletions(-) diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 11fa31afb2f..2fa416b611e 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -106,7 +106,7 @@ impl Eval { .environments .has_lex_binding_until_function_environment(&vars) { - let name = context.interner().resolve_expect(name); + let name = context.interner().resolve_expect(name.sym()); let msg = format!("variable declaration `{name}` in eval function already exists as lexically declaration"); return context.throw_syntax_error(msg); } diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 149f402f4ad..c3c70e077fb 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -591,7 +591,7 @@ impl BuiltInFunctionObject { { return context.throw_syntax_error(format!( "Redeclaration of formal parameter `{}`", - context.interner().resolve_expect(param_name) + context.interner().resolve_expect(param_name.sym()) )); } } diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index 1203c7d1204..d4b661cc8d6 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -112,11 +112,11 @@ impl FunctionCompiler { if !(self.arrow) && !parameters.has_arguments() { compiler .context - .create_mutable_binding(Sym::ARGUMENTS, false); + .create_mutable_binding(Sym::ARGUMENTS.into(), false); compiler.code_block.arguments_binding = Some( compiler .context - .initialize_mutable_binding(Sym::ARGUMENTS, false), + .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), ); } @@ -125,21 +125,27 @@ impl FunctionCompiler { compiler.emit_opcode(Opcode::RestParameterInit); } - if let Some(init) = parameter.declaration().init() { - let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - compiler.compile_expr(init, true)?; - compiler.patch_jump(skip); - } - match parameter.declaration().binding() { Binding::Identifier(ident) => { - compiler.context.create_mutable_binding(ident.sym(), false); - compiler.emit_binding(BindingOpcode::InitArg, ident.sym()); + compiler.context.create_mutable_binding(*ident, false); + // TODO: throw custom error if ident is in init + if let Some(init) = parameter.declaration().init() { + let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); + compiler.compile_expr(init, true)?; + compiler.patch_jump(skip); + } + compiler.emit_binding(BindingOpcode::InitArg, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { compiler.context.create_mutable_binding(ident, false); } + // TODO: throw custom error if ident is in init + if let Some(init) = parameter.declaration().init() { + let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); + compiler.compile_expr(init, true)?; + compiler.patch_jump(skip); + } compiler.compile_declaration_pattern(pattern, BindingOpcode::InitArg)?; } } diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 1985db4208f..a7bbcc3a1d2 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -11,7 +11,7 @@ use crate::{ binary::op::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp}, unary::op::UnaryOp, }, - Call, New, + Call, Identifier, New, }, function::{ ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList, @@ -57,7 +57,7 @@ enum FunctionKind { #[derive(Debug, Clone, Copy, PartialEq)] struct FunctionSpec<'a> { kind: FunctionKind, - name: Option, + name: Option, parameters: &'a FormalParameterList, body: &'a StatementList, } @@ -176,7 +176,7 @@ enum JumpControlInfoKind { #[derive(Debug, Clone, Copy)] enum Access<'a> { - Variable { name: Sym }, + Variable { name: Identifier }, Property { access: &'a PropertyAccess }, PrivateProperty { access: &'a PrivatePropertyAccess }, SuperProperty { field: &'a PropertyAccessField }, @@ -187,7 +187,7 @@ enum Access<'a> { pub struct ByteCompiler<'b> { code_block: CodeBlock, literals_map: FxHashMap, - names_map: FxHashMap, + names_map: FxHashMap, bindings_map: FxHashMap, jump_info: Vec, in_async_generator: bool, @@ -245,7 +245,7 @@ impl<'b> ByteCompiler<'b> { } #[inline] - fn get_or_insert_name(&mut self, name: Sym) -> u32 { + fn get_or_insert_name(&mut self, name: Identifier) -> u32 { if let Some(index) = self.names_map.get(&name) { return *index; } @@ -269,7 +269,7 @@ impl<'b> ByteCompiler<'b> { } #[inline] - fn emit_binding(&mut self, opcode: BindingOpcode, name: Sym) { + fn emit_binding(&mut self, opcode: BindingOpcode, name: Identifier) { match opcode { BindingOpcode::Var => { let binding = self.context.initialize_mutable_binding(name, true); @@ -640,7 +640,7 @@ impl<'b> ByteCompiler<'b> { #[inline] fn compile_access(expr: &Expression) -> Option> { match expr { - Expression::Identifier(name) => Some(Access::Variable { name: name.sym() }), + Expression::Identifier(name) => Some(Access::Variable { name: *name }), Expression::PropertyAccess(access) => Some(Access::Property { access }), Expression::This => Some(Access::This), _ => None, @@ -657,7 +657,7 @@ impl<'b> ByteCompiler<'b> { } Access::Property { access } => match access.field() { PropertyAccessField::Const(name) => { - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.compile_expr(access.target(), true)?; self.emit(Opcode::GetPropertyByName, &[index]); } @@ -668,13 +668,13 @@ impl<'b> ByteCompiler<'b> { } }, Access::PrivateProperty { access } => { - let index = self.get_or_insert_name(access.field()); + let index = self.get_or_insert_name(access.field().into()); self.compile_expr(access.target(), true)?; self.emit(Opcode::GetPrivateField, &[index]); } Access::SuperProperty { field } => match field { PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name(*field); + let index = self.get_or_insert_name((*field).into()); self.emit_opcode(Opcode::Super); self.emit(Opcode::GetPropertyByName, &[index]); } @@ -719,7 +719,7 @@ impl<'b> ByteCompiler<'b> { Access::Property { access } => match access.field() { PropertyAccessField::Const(name) => { self.compile_expr(access.target(), true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPropertyByName, &[index]); } PropertyAccessField::Expr(expr) => { @@ -731,7 +731,7 @@ impl<'b> ByteCompiler<'b> { Access::PrivateProperty { access } => { self.compile_expr(access.target(), true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(access.field()); + let index = self.get_or_insert_name(access.field().into()); self.emit(Opcode::AssignPrivateField, &[index]); } // TODO: access_set `super` @@ -856,7 +856,7 @@ impl<'b> ByteCompiler<'b> { Expression::PropertyAccess(ref access) => { match access.field() { PropertyAccessField::Const(name) => { - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.compile_expr(access.target(), true)?; self.emit(Opcode::DeletePropertyByName, &[index]); } @@ -885,7 +885,7 @@ impl<'b> ByteCompiler<'b> { UnaryOp::TypeOf => { match &unary.target() { Expression::Identifier(identifier) => { - let binding = self.context.get_binding_value(identifier.sym()); + let binding = self.context.get_binding_value(*identifier); let index = self.get_or_insert_binding(binding); self.emit(Opcode::GetNameOrUndefined, &[index]); } @@ -995,7 +995,7 @@ impl<'b> ByteCompiler<'b> { } Expression::Assign(assign) if assign.op() == AssignOp::Assign => match assign.lhs() { AssignTarget::Identifier(name) => self.access_set( - Access::Variable { name: name.sym() }, + Access::Variable { name: *name }, Some(assign.rhs()), use_expr, )?, @@ -1026,7 +1026,7 @@ impl<'b> ByteCompiler<'b> { }, Expression::Assign(assign) => { let access = match assign.lhs() { - AssignTarget::Identifier(name) => Access::Variable { name: name.sym() }, + AssignTarget::Identifier(name) => Access::Variable { name: *name }, AssignTarget::Property(access) => Access::Property { access }, AssignTarget::PrivateProperty(access) => Access::PrivateProperty { access }, AssignTarget::SuperProperty(access) => Access::SuperProperty { @@ -1091,7 +1091,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.compile_expr(expr, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1101,12 +1101,12 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::DefineOwnPropertyByValue); } }, - PropertyDefinition::MethodDefinition(kind, name) => match kind { + PropertyDefinition::MethodDefinition(name, kind) => match kind { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPropertyGetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1120,7 +1120,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPropertySetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1134,7 +1134,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1148,7 +1148,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1162,7 +1162,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1176,7 +1176,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -1206,7 +1206,7 @@ impl<'b> ByteCompiler<'b> { } } Expression::Identifier(name) => { - self.access_get(Access::Variable { name: name.sym() }, use_expr)?; + self.access_get(Access::Variable { name: *name }, use_expr)?; } Expression::PropertyAccess(access) => { self.access_get(Access::Property { access }, use_expr)?; @@ -1351,7 +1351,7 @@ impl<'b> ByteCompiler<'b> { self.emit(Opcode::Dup, &[]); match access.field() { PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name(*field); + let index = self.get_or_insert_name((*field).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyAccessField::Expr(field) => { @@ -1390,7 +1390,7 @@ impl<'b> ByteCompiler<'b> { } self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(Sym::RAW); + let index = self.get_or_insert_name(Sym::RAW.into()); self.emit(Opcode::SetPropertyByName, &[index]); for expr in template.exprs() { @@ -1456,7 +1456,7 @@ impl<'b> ByteCompiler<'b> { .init() .expect("const declaration must have initializer"); self.compile_expr(init, true)?; - self.emit_binding(BindingOpcode::InitConst, ident.sym()); + self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { if let Some(init) = decl.init() { @@ -1475,12 +1475,12 @@ impl<'b> ByteCompiler<'b> { for decl in list.as_ref() { match decl.binding() { Binding::Identifier(ident) => { - let ident = ident.sym(); + let ident = ident; if let Some(expr) = decl.init() { self.compile_expr(expr, true)?; - self.emit_binding(init_op, ident); + self.emit_binding(init_op, *ident); } else { - self.emit_binding(empty_op, ident); + self.emit_binding(empty_op, *ident); } } Binding::Pattern(pattern) => { @@ -1616,15 +1616,15 @@ impl<'b> ByteCompiler<'b> { match for_in_loop.init() { IterableLoopInitializer::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), true); - let binding = self.context.set_mutable_binding(ident.sym()); + self.context.create_mutable_binding(*ident, true); + let binding = self.context.set_mutable_binding(*ident); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), true); - self.emit_binding(BindingOpcode::InitVar, ident.sym()); + self.context.create_mutable_binding(*ident, true); + self.emit_binding(BindingOpcode::InitVar, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -1635,8 +1635,8 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), false); - self.emit_binding(BindingOpcode::InitLet, ident.sym()); + self.context.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -1647,8 +1647,8 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(ident.sym()); - self.emit_binding(BindingOpcode::InitConst, ident.sym()); + self.context.create_immutable_binding(*ident); + self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -1726,15 +1726,15 @@ impl<'b> ByteCompiler<'b> { match for_of_loop.init() { IterableLoopInitializer::Identifier(ref ident) => { - self.context.create_mutable_binding(ident.sym(), true); - let binding = self.context.set_mutable_binding(ident.sym()); + self.context.create_mutable_binding(*ident, true); + let binding = self.context.set_mutable_binding(*ident); let index = self.get_or_insert_binding(binding); self.emit(Opcode::DefInitVar, &[index]); } IterableLoopInitializer::Var(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), true); - self.emit_binding(BindingOpcode::InitVar, ident.sym()); + self.context.create_mutable_binding(*ident, true); + self.emit_binding(BindingOpcode::InitVar, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -1745,8 +1745,8 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Let(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), false); - self.emit_binding(BindingOpcode::InitLet, ident.sym()); + self.context.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -1757,8 +1757,8 @@ impl<'b> ByteCompiler<'b> { }, IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { - self.context.create_immutable_binding(ident.sym()); - self.emit_binding(BindingOpcode::InitConst, ident.sym()); + self.context.create_immutable_binding(*ident); + self.emit_binding(BindingOpcode::InitConst, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2080,8 +2080,8 @@ impl<'b> ByteCompiler<'b> { if let Some(binding) = catch.parameter() { match binding { Binding::Identifier(ident) => { - self.context.create_mutable_binding(ident.sym(), false); - self.emit_binding(BindingOpcode::InitLet, ident.sym()); + self.context.create_mutable_binding(*ident, false); + self.emit_binding(BindingOpcode::InitLet, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2176,7 +2176,7 @@ impl<'b> ByteCompiler<'b> { } = function; let code = FunctionCompiler::new() - .name(name) + .name(name.map(Identifier::sym)) .generator(generator) .r#async(r#async) .strict(self.code_block.strict) @@ -2223,9 +2223,7 @@ impl<'b> ByteCompiler<'b> { let (call, kind) = match callable { Callable::Call(call) => match call.expr() { - Expression::Identifier(ident) if ident.sym() == Sym::EVAL => { - (call, CallKind::CallEval) - } + Expression::Identifier(ident) if *ident == Sym::EVAL => (call, CallKind::CallEval), _ => (call, CallKind::Call), }, Callable::New(new) => (new.call(), CallKind::New), @@ -2239,7 +2237,7 @@ impl<'b> ByteCompiler<'b> { } match access.field() { PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name(*field); + let index = self.get_or_insert_name((*field).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyAccessField::Expr(field) => { @@ -2256,7 +2254,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Super); match access.field() { PropertyAccessField::Const(field) => { - let index = self.get_or_insert_name(*field); + let index = self.get_or_insert_name((*field).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyAccessField::Expr(expr) => { @@ -2271,7 +2269,7 @@ impl<'b> ByteCompiler<'b> { if kind == CallKind::Call { self.emit(Opcode::Dup, &[]); } - let index = self.get_or_insert_name(access.field()); + let index = self.get_or_insert_name(access.field().into()); self.emit(Opcode::GetPrivateField, &[index]); } expr => { @@ -2358,7 +2356,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); match name { PropertyName::Literal(name) => { - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyName::Computed(node) => { @@ -2394,7 +2392,7 @@ impl<'b> ByteCompiler<'b> { for key in excluded_keys { self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*key).into_common(false), + self.interner().resolve_expect(key.sym()).into_common(false), )); } @@ -2412,7 +2410,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::PushEmptyObject); for key in excluded_keys { self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*key).into_common(false), + self.interner().resolve_expect(key.sym()).into_common(false), )); } self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32, 0]); @@ -2426,7 +2424,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); match name { PropertyName::Literal(name) => { - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyName::Computed(node) => { @@ -2462,7 +2460,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::Dup); match name { PropertyName::Literal(name) => { - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::GetPropertyByName, &[index]); } PropertyName::Computed(node) => { @@ -2577,11 +2575,11 @@ impl<'b> ByteCompiler<'b> { for decl in &**list { match decl.binding() { Binding::Identifier(ident) => { - let ident = ident.sym(); - if ident == Sym::ARGUMENTS { + let ident = ident; + if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_mutable_binding(ident, true); + self.context.create_mutable_binding(*ident, true); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2598,11 +2596,11 @@ impl<'b> ByteCompiler<'b> { for decl in &**list { match decl.binding() { Binding::Identifier(ident) => { - let ident = ident.sym(); - if ident == Sym::ARGUMENTS { + let ident = ident; + if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_mutable_binding(ident, false); + self.context.create_mutable_binding(*ident, false); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2619,11 +2617,11 @@ impl<'b> ByteCompiler<'b> { for decl in &**list { match decl.binding() { Binding::Identifier(ident) => { - let ident = ident.sym(); - if ident == Sym::ARGUMENTS { + let ident = ident; + if *ident == Sym::ARGUMENTS { has_identifier_argument = true; } - self.context.create_immutable_binding(ident); + self.context.create_immutable_binding(*ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2701,7 +2699,11 @@ impl<'b> ByteCompiler<'b> { /// A class declaration binds the resulting class object to it's identifier. /// A class expression leaves the resulting class object on the stack for following operations. fn class(&mut self, class: &Class, expression: bool) -> JsResult<()> { - let code = CodeBlock::new(class.name().unwrap_or(Sym::EMPTY_STRING), 0, true); + let code = CodeBlock::new( + class.name().map_or(Sym::EMPTY_STRING, Identifier::sym), + 0, + true, + ); let mut compiler = ByteCompiler { code_block: code, literals_map: FxHashMap::default(), @@ -2718,11 +2720,11 @@ impl<'b> ByteCompiler<'b> { compiler.code_block.params = expr.parameters().clone(); compiler .context - .create_mutable_binding(Sym::ARGUMENTS, false); + .create_mutable_binding(Sym::ARGUMENTS.into(), false); compiler.code_block.arguments_binding = Some( compiler .context - .initialize_mutable_binding(Sym::ARGUMENTS, false), + .initialize_mutable_binding(Sym::ARGUMENTS.into(), false), ); for parameter in expr.parameters().parameters.iter() { if parameter.is_rest_param() { @@ -2731,14 +2733,14 @@ impl<'b> ByteCompiler<'b> { match parameter.declaration().binding() { Binding::Identifier(ident) => { - compiler.context.create_mutable_binding(ident.sym(), false); + compiler.context.create_mutable_binding(*ident, false); if let Some(init) = parameter.declaration().init() { let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); compiler.compile_expr(init, true)?; compiler.patch_jump(skip); } - compiler.emit_binding(BindingOpcode::InitArg, ident.sym()); + compiler.emit_binding(BindingOpcode::InitArg, *ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -2822,7 +2824,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassGetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2836,7 +2838,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassSetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2850,7 +2852,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2864,7 +2866,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2878,7 +2880,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2892,7 +2894,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -2909,32 +2911,32 @@ impl<'b> ByteCompiler<'b> { match method_definition { MethodDefinition::Get(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateMethod, &[index]); } } @@ -2984,7 +2986,7 @@ impl<'b> ByteCompiler<'b> { } ClassElement::PrivateFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); - let name_index = self.get_or_insert_name(*name); + let name_index = self.get_or_insert_name((*name).into()); let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); let mut field_compiler = ByteCompiler { code_block: field_code, @@ -3026,7 +3028,7 @@ impl<'b> ByteCompiler<'b> { self.emit_opcode(Opcode::PushUndefined); } self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineOwnPropertyByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3048,7 +3050,7 @@ impl<'b> ByteCompiler<'b> { } else { self.emit_opcode(Opcode::PushUndefined); } - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPrivateField, &[index]); } ClassElement::StaticBlock(statement_list) => { @@ -3078,32 +3080,32 @@ impl<'b> ByteCompiler<'b> { match method_definition { MethodDefinition::Get(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::PushClassPrivateMethod, &[index]); } } @@ -3123,7 +3125,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassGetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3137,7 +3139,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassSetterByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3151,7 +3153,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3165,7 +3167,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3179,7 +3181,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { @@ -3193,7 +3195,7 @@ impl<'b> ByteCompiler<'b> { PropertyName::Literal(name) => { self.function(expr.into(), NodeKind::Expression, true)?; self.emit_opcode(Opcode::Swap); - let index = self.get_or_insert_name(*name); + let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 99bec72afce..94ef698903f 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -1,8 +1,9 @@ use crate::{ - environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue, + environments::runtime::BindingLocator, property::PropertyDescriptor, + syntax::ast::expression::Identifier, Context, JsString, JsValue, }; use boa_gc::{Cell, Finalize, Gc, Trace}; -use boa_interner::Sym; + use rustc_hash::FxHashMap; /// A compile time binding represents a binding at bytecode compile time in a [`CompileTimeEnvironment`]. @@ -23,7 +24,7 @@ pub(crate) struct CompileTimeEnvironment { outer: Option>>, environment_index: usize, #[unsafe_ignore_trace] - bindings: FxHashMap, + bindings: FxHashMap, function_scope: bool, } @@ -41,7 +42,7 @@ impl CompileTimeEnvironment { /// Check if environment has a lexical binding with the given name. #[inline] - pub(crate) fn has_lex_binding(&self, name: Sym) -> bool { + pub(crate) fn has_lex_binding(&self, name: Identifier) -> bool { self.bindings .get(&name) .map_or(false, |binding| binding.lex) @@ -61,7 +62,7 @@ impl CompileTimeEnvironment { /// Get the locator for a binding name. #[inline] - pub(crate) fn get_binding(&self, name: Sym) -> Option { + pub(crate) fn get_binding(&self, name: Identifier) -> Option { self.bindings .get(&name) .map(|binding| BindingLocator::declarative(name, self.environment_index, binding.index)) @@ -69,7 +70,7 @@ impl CompileTimeEnvironment { /// Get the locator for a binding name in this and all outer environments. #[inline] - pub(crate) fn get_binding_recursive(&self, name: Sym) -> BindingLocator { + pub(crate) fn get_binding_recursive(&self, name: Identifier) -> BindingLocator { if let Some(binding) = self.bindings.get(&name) { BindingLocator::declarative(name, self.environment_index, binding.index) } else if let Some(outer) = &self.outer { @@ -81,7 +82,7 @@ impl CompileTimeEnvironment { /// Check if a binding name exists in this and all outer environments. #[inline] - pub(crate) fn has_binding_recursive(&self, name: Sym) -> bool { + pub(crate) fn has_binding_recursive(&self, name: Identifier) -> bool { if self.bindings.contains_key(&name) { true } else if let Some(outer) = &self.outer { @@ -95,7 +96,11 @@ impl CompileTimeEnvironment { /// /// If the binding is a function scope binding and this is a declarative environment, try the outer environment. #[inline] - pub(crate) fn create_mutable_binding(&mut self, name: Sym, function_scope: bool) -> bool { + pub(crate) fn create_mutable_binding( + &mut self, + name: Identifier, + function_scope: bool, + ) -> bool { if let Some(outer) = &self.outer { if !function_scope || self.function_scope { if !self.bindings.contains_key(&name) { @@ -135,7 +140,7 @@ impl CompileTimeEnvironment { /// Crate an immutable binding. #[inline] - pub(crate) fn create_immutable_binding(&mut self, name: Sym) { + pub(crate) fn create_immutable_binding(&mut self, name: Identifier) { let binding_index = self.bindings.len(); self.bindings.insert( name, @@ -151,7 +156,7 @@ impl CompileTimeEnvironment { #[inline] pub(crate) fn initialize_mutable_binding( &self, - name: Sym, + name: Identifier, function_scope: bool, ) -> BindingLocator { if let Some(outer) = &self.outer { @@ -180,14 +185,14 @@ impl CompileTimeEnvironment { /// /// Panics if the binding is not in the current environment. #[inline] - pub(crate) fn initialize_immutable_binding(&self, name: Sym) -> BindingLocator { + pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator { let binding = self.bindings.get(&name).expect("binding must exist"); BindingLocator::declarative(name, self.environment_index, binding.index) } /// Return the binding locator for a mutable binding. #[inline] - pub(crate) fn set_mutable_binding_recursive(&self, name: Sym) -> BindingLocator { + pub(crate) fn set_mutable_binding_recursive(&self, name: Identifier) -> BindingLocator { match self.bindings.get(&name) { Some(binding) if binding.mutable => { BindingLocator::declarative(name, self.environment_index, binding.index) @@ -261,7 +266,7 @@ impl Context { /// /// Note: This function only works at bytecode compile time! #[inline] - pub(crate) fn get_binding_value(&self, name: Sym) -> BindingLocator { + pub(crate) fn get_binding_value(&self, name: Identifier) -> BindingLocator { self.realm.compile_env.borrow().get_binding_recursive(name) } @@ -270,7 +275,7 @@ impl Context { /// /// Note: This function only works at bytecode compile time! #[inline] - pub(crate) fn has_binding(&self, name: Sym) -> bool { + pub(crate) fn has_binding(&self, name: Identifier) -> bool { self.realm.compile_env.borrow().has_binding_recursive(name) } @@ -283,7 +288,7 @@ impl Context { /// /// Panics if the global environment is not function scoped. #[inline] - pub(crate) fn create_mutable_binding(&mut self, name: Sym, function_scope: bool) { + pub(crate) fn create_mutable_binding(&mut self, name: Identifier, function_scope: bool) { if !self .realm .compile_env @@ -292,7 +297,7 @@ impl Context { { let name_str = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false); let desc = self .realm @@ -319,7 +324,7 @@ impl Context { #[inline] pub(crate) fn initialize_mutable_binding( &self, - name: Sym, + name: Identifier, function_scope: bool, ) -> BindingLocator { self.realm @@ -337,7 +342,7 @@ impl Context { /// /// Panics if the global environment does not exist. #[inline] - pub(crate) fn create_immutable_binding(&mut self, name: Sym) { + pub(crate) fn create_immutable_binding(&mut self, name: Identifier) { self.realm .compile_env .borrow_mut() @@ -352,7 +357,7 @@ impl Context { /// /// Panics if the global environment does not exist or a the binding was not created on the current environment. #[inline] - pub(crate) fn initialize_immutable_binding(&self, name: Sym) -> BindingLocator { + pub(crate) fn initialize_immutable_binding(&self, name: Identifier) -> BindingLocator { self.realm .compile_env .borrow() @@ -363,7 +368,7 @@ impl Context { /// /// Note: This function only works at bytecode compile time! #[inline] - pub(crate) fn set_mutable_binding(&self, name: Sym) -> BindingLocator { + pub(crate) fn set_mutable_binding(&self, name: Identifier) -> BindingLocator { self.realm .compile_env .borrow() diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 21ecf3aed0f..06acff567bd 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -1,6 +1,9 @@ -use crate::{environments::CompileTimeEnvironment, object::JsObject, Context, JsResult, JsValue}; +use crate::{ + environments::CompileTimeEnvironment, object::JsObject, syntax::ast::expression::Identifier, + Context, JsResult, JsValue, +}; use boa_gc::{Cell, Finalize, Gc, Trace}; -use boa_interner::Sym; + use rustc_hash::FxHashSet; /// A declarative environment holds binding values at runtime. @@ -259,8 +262,8 @@ impl DeclarativeEnvironmentStack { /// Stop at the next outer function environment. pub(crate) fn has_lex_binding_until_function_environment( &self, - names: &FxHashSet, - ) -> Option { + names: &FxHashSet, + ) -> Option { for env in self.stack.iter().rev() { let compile = env.compile.borrow(); for name in names { @@ -517,7 +520,7 @@ impl DeclarativeEnvironmentStack { &self, mut environment_index: usize, mut binding_index: usize, - name: Sym, + name: Identifier, ) -> Option { if environment_index != self.stack.len() - 1 { for env_index in (environment_index + 1..self.stack.len()).rev() { @@ -554,7 +557,7 @@ impl DeclarativeEnvironmentStack { /// This only considers function environments that are poisoned. /// All other bindings are accessed via indices. #[inline] - pub(crate) fn get_value_global_poisoned(&self, name: Sym) -> Option { + pub(crate) fn get_value_global_poisoned(&self, name: Identifier) -> Option { for env in self.stack.iter().rev() { if !*env.poisoned.borrow() { return None; @@ -612,7 +615,7 @@ impl DeclarativeEnvironmentStack { &mut self, mut environment_index: usize, mut binding_index: usize, - name: Sym, + name: Identifier, value: JsValue, ) -> bool { if environment_index != self.stack.len() - 1 { @@ -687,7 +690,7 @@ impl DeclarativeEnvironmentStack { /// /// Panics if the environment or binding index are out of range. #[inline] - pub(crate) fn put_value_global_poisoned(&mut self, name: Sym, value: &JsValue) -> bool { + pub(crate) fn put_value_global_poisoned(&mut self, name: Identifier, value: &JsValue) -> bool { for env in self.stack.iter().rev() { if !*env.poisoned.borrow() { return false; @@ -718,7 +721,7 @@ impl DeclarativeEnvironmentStack { /// Binding locators get created at bytecode compile time and are accessible at runtime via the [`crate::vm::CodeBlock`]. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(crate) struct BindingLocator { - name: Sym, + name: Identifier, environment_index: usize, binding_index: usize, global: bool, @@ -729,7 +732,7 @@ impl BindingLocator { /// Creates a new declarative binding locator that has knows indices. #[inline] pub(in crate::environments) fn declarative( - name: Sym, + name: Identifier, environment_index: usize, binding_index: usize, ) -> Self { @@ -744,7 +747,7 @@ impl BindingLocator { /// Creates a binding locator that indicates that the binding is on the global object. #[inline] - pub(in crate::environments) fn global(name: Sym) -> Self { + pub(in crate::environments) fn global(name: Identifier) -> Self { Self { name, environment_index: 0, @@ -757,7 +760,7 @@ impl BindingLocator { /// Creates a binding locator that indicates that it was attempted to mutate an immutable binding. /// At runtime this should always produce a type error. #[inline] - pub(in crate::environments) fn mutate_immutable(name: Sym) -> Self { + pub(in crate::environments) fn mutate_immutable(name: Identifier) -> Self { Self { name, environment_index: 0, @@ -769,7 +772,7 @@ impl BindingLocator { /// Returns the name of the binding. #[inline] - pub(crate) fn name(&self) -> Sym { + pub(crate) fn name(&self) -> Identifier { self.name } @@ -797,7 +800,7 @@ impl BindingLocator { if self.mutate_immutable { context.throw_type_error(format!( "cannot mutate an immutable binding '{}'", - context.interner().resolve_expect(self.name) + context.interner().resolve_expect(self.name.sym()) )) } else { Ok(()) diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_engine/src/syntax/ast/expression/access.rs index b69d22564ea..4fa8aea6d80 100644 --- a/boa_engine/src/syntax/ast/expression/access.rs +++ b/boa_engine/src/syntax/ast/expression/access.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use boa_interner::{Interner, Sym, ToInternedString}; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] @@ -8,6 +8,22 @@ pub enum PropertyAccessField { Expr(Box), } +impl PropertyAccessField { + pub(crate) fn contains_arguments(&self) -> bool { + match self { + PropertyAccessField::Const(_) => false, + PropertyAccessField::Expr(expr) => expr.contains_arguments(), + } + } + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + PropertyAccessField::Const(_) => false, + PropertyAccessField::Expr(expr) => expr.contains(symbol), + } + } +} + impl From for PropertyAccessField { fn from(id: Sym) -> Self { Self::Const(id) @@ -68,6 +84,16 @@ impl PropertyAccess { field: field.into(), } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.target.contains_arguments() || self.field.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.target.contains(symbol) || self.field.contains(symbol) + } } impl ToInternedString for PropertyAccess { @@ -124,6 +150,15 @@ impl PrivatePropertyAccess { pub fn field(&self) -> Sym { self.field } + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.target.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.target.contains(symbol) + } } impl ToInternedString for PrivatePropertyAccess { @@ -165,6 +200,16 @@ impl SuperPropertyAccess { pub fn field(&self) -> &PropertyAccessField { &self.field } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.field.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.field.contains(symbol) + } } impl ToInternedString for SuperPropertyAccess { diff --git a/boa_engine/src/syntax/ast/expression/await.rs b/boa_engine/src/syntax/ast/expression/await.rs index 42b50a7086d..0e49c4c6a32 100644 --- a/boa_engine/src/syntax/ast/expression/await.rs +++ b/boa_engine/src/syntax/ast/expression/await.rs @@ -1,5 +1,7 @@ //! Await expression Expression. +use crate::syntax::ast::ContainsSymbol; + use super::Expression; use boa_interner::{Interner, ToInternedString}; @@ -23,6 +25,16 @@ impl Await { pub(crate) fn expr(&self) -> &Expression { &self.expr } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.expr.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.expr.contains(symbol) + } } impl From for Await diff --git a/boa_engine/src/syntax/ast/expression/call.rs b/boa_engine/src/syntax/ast/expression/call.rs index ebef884e93e..eac8c0245dd 100644 --- a/boa_engine/src/syntax/ast/expression/call.rs +++ b/boa_engine/src/syntax/ast/expression/call.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::join_nodes; +use crate::syntax::ast::{join_nodes, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use super::Expression; @@ -42,6 +42,16 @@ impl Call { pub fn args(&self) -> &[Expression] { &self.args } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.target.contains_arguments() || self.args.iter().any(Expression::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.target.contains(symbol) || self.args.iter().any(|expr| expr.contains(symbol)) + } } impl ToInternedString for Call { @@ -87,6 +97,16 @@ impl SuperCall { pub(crate) fn args(&self) -> &[Expression] { &self.args } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.args.iter().any(Expression::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.args.iter().any(|expr| expr.contains(symbol)) + } } impl ToInternedString for SuperCall { diff --git a/boa_engine/src/syntax/ast/expression/identifier.rs b/boa_engine/src/syntax/ast/expression/identifier.rs index 26db2cb4522..2a89b48d3c3 100644 --- a/boa_engine/src/syntax/ast/expression/identifier.rs +++ b/boa_engine/src/syntax/ast/expression/identifier.rs @@ -26,11 +26,24 @@ use super::Expression; /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(transparent)] pub struct Identifier { ident: Sym, } +impl PartialEq for Identifier { + fn eq(&self, other: &Sym) -> bool { + self.ident == *other + } +} + +impl PartialEq for Sym { + fn eq(&self, other: &Identifier) -> bool { + *self == other.ident + } +} + impl Identifier { /// Creates a new identifier AST Expression. pub fn new(ident: Sym) -> Self { diff --git a/boa_engine/src/syntax/ast/expression/literal/array.rs b/boa_engine/src/syntax/ast/expression/literal/array.rs index 29f03f4e8d1..8516359521b 100644 --- a/boa_engine/src/syntax/ast/expression/literal/array.rs +++ b/boa_engine/src/syntax/ast/expression/literal/array.rs @@ -1,6 +1,6 @@ //! Array declaration Expression. -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; /// An array is an ordered collection of data (either primitive or object depending upon the @@ -43,6 +43,19 @@ impl ArrayLiteral { 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 { diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs index d071a782ac5..82dfe4289e4 100644 --- a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs +++ b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs @@ -4,6 +4,7 @@ mod tests; use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::ContainsSymbol; use crate::syntax::ast::property::MethodDefinition; use crate::syntax::ast::property::PropertyDefinition; @@ -48,7 +49,7 @@ impl ObjectLiteral { for property in self.properties().iter() { buf.push_str(&match property { PropertyDefinition::IdentifierReference(ident) => { - format!("{indentation}{},\n", interner.resolve_expect(*ident)) + format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) } PropertyDefinition::Property(key, value) => { format!( @@ -60,7 +61,7 @@ impl ObjectLiteral { PropertyDefinition::SpreadObject(key) => { format!("{indentation}...{},\n", key.to_interned_string(interner)) } - PropertyDefinition::MethodDefinition(method, key) => { + PropertyDefinition::MethodDefinition(key, method) => { format!( "{indentation}{}{}({}) {},\n", match &method { @@ -106,7 +107,7 @@ impl ObjectLiteral { PropertyDefinition::CoverInitializedName(ident, expr) => { format!( "{indentation}{} = {},\n", - interner.resolve_expect(*ident), + interner.resolve_expect(ident.sym()), expr.to_no_indent_string(interner, indent_n + 1) ) } @@ -116,6 +117,18 @@ impl ObjectLiteral { buf } + + #[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 ToInternedString for ObjectLiteral { diff --git a/boa_engine/src/syntax/ast/expression/literal/template.rs b/boa_engine/src/syntax/ast/expression/literal/template.rs index b4b79ff7444..ea2002fe4ef 100644 --- a/boa_engine/src/syntax/ast/expression/literal/template.rs +++ b/boa_engine/src/syntax/ast/expression/literal/template.rs @@ -4,7 +4,10 @@ use std::borrow::Cow; use boa_interner::{Interner, Sym, ToInternedString}; -use crate::{string::ToStringEscaped, syntax::ast::expression::Expression}; +use crate::{ + string::ToStringEscaped, + syntax::ast::{expression::Expression, ContainsSymbol}, +}; /// Template literals are string literals allowing embedded expressions. /// @@ -41,6 +44,22 @@ impl TemplateLiteral { pub(crate) fn elements(&self) -> &[TemplateElement] { &self.elements } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.elements.iter().any(|e| match e { + TemplateElement::String(_) => false, + TemplateElement::Expr(expr) => expr.contains_arguments(), + }) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.elements.iter().any(|e| match e { + TemplateElement::String(_) => false, + TemplateElement::Expr(expr) => expr.contains(symbol), + }) + } } impl ToInternedString for TemplateLiteral { diff --git a/boa_engine/src/syntax/ast/expression/mod.rs b/boa_engine/src/syntax/ast/expression/mod.rs index 579ab0359ad..817948792ef 100644 --- a/boa_engine/src/syntax/ast/expression/mod.rs +++ b/boa_engine/src/syntax/ast/expression/mod.rs @@ -1,17 +1,14 @@ use boa_interner::{Interner, Sym, ToInternedString}; use self::{ - access::{PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SuperPropertyAccess}, - literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral}, - operator::{assign::AssignTarget, conditional::Conditional, Assign, Binary, Unary}, + access::{PrivatePropertyAccess, PropertyAccess, SuperPropertyAccess}, + literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateLiteral}, + operator::{conditional::Conditional, Assign, Binary, Unary}, }; use super::{ function::FormalParameterList, - function::{ - ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator, - }, - property::{MethodDefinition, PropertyDefinition}, + function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator}, ContainsSymbol, Statement, }; @@ -194,212 +191,39 @@ impl Expression { /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments // TODO: replace with a visitor + #[inline] pub(crate) fn contains_arguments(&self) -> bool { match self { - Expression::Identifier(ident) if ident.sym() == Sym::ARGUMENTS => return true, - Expression::ArrayLiteral(array) => { - for expr in array.as_ref() { - if matches!(expr, Some(expr) if expr.contains_arguments()) { - return true; - } - } - } - Expression::ObjectLiteral(object) => { - for property in object.properties() { - match property { - PropertyDefinition::IdentifierReference(ident) => { - if *ident == Sym::ARGUMENTS { - return true; - } - } - PropertyDefinition::Property(_, node) - | PropertyDefinition::SpreadObject(node) => { - if node.contains_arguments() { - return true; - } - } - PropertyDefinition::MethodDefinition(method, _) => match method { - MethodDefinition::Get(function) - | MethodDefinition::Set(function) - | MethodDefinition::Ordinary(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - MethodDefinition::Generator(generator) => { - if let Some(Sym::ARGUMENTS) = generator.name() { - return true; - } - } - MethodDefinition::AsyncGenerator(async_generator) => { - if let Some(Sym::ARGUMENTS) = async_generator.name() { - return true; - } - } - MethodDefinition::Async(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - }, - PropertyDefinition::CoverInitializedName(_, _) => {} - } - } - } - Expression::Spread(spread) => { - if spread.val().contains_arguments() { - return true; - } - } - Expression::Assign(assign) => { - if assign.rhs().contains_arguments() { - return true; - } - } - Expression::Await(r#await) => { - if r#await.expr().contains_arguments() { - return true; - } - } - Expression::Binary(bin_op) => { - if bin_op.lhs().contains_arguments() || bin_op.rhs().contains_arguments() { - return true; - } - } - Expression::Call(call) => { - if call.expr().contains_arguments() { - return true; - } - for node in call.args() { - if node.contains_arguments() { - return true; - } - } - } - Expression::Conditional(conditional) => { - if conditional.cond().contains_arguments() { - return true; - } - if conditional.if_true().contains_arguments() { - return true; - } - if conditional.if_false().contains_arguments() { - return true; - } - } - Expression::PropertyAccess(access) => { - if access.target().contains_arguments() { - return true; - } - if let PropertyAccessField::Expr(expr) = access.field() { - if expr.contains_arguments() { - return true; - } - } - } - Expression::PrivatePropertyAccess(access) => { - if access.target().contains_arguments() { - return true; - } - } - Expression::New(new) => { - if new.expr().contains_arguments() { - return true; - } - for node in new.args() { - if node.contains_arguments() { - return true; - } - } - } - Expression::TaggedTemplate(tagged_template) => { - if tagged_template.tag().contains_arguments() { - return true; - } - for node in tagged_template.exprs() { - if node.contains_arguments() { - return true; - } - } - } - Expression::TemplateLiteral(template_lit) => { - for element in template_lit.elements() { - if let TemplateElement::Expr(node) = element { - if node.contains_arguments() { - return false; - } - } - } - } - Expression::Unary(unary_op) => { - if unary_op.target().contains_arguments() { - return true; - } - } - Expression::Yield(r#yield) => { - if let Some(node) = r#yield.expr() { - if node.contains_arguments() { - return true; - } - } - } - Expression::Class(class) => { - if let Some(node) = class.super_ref() { - if node.contains_arguments() { - return true; - } - for element in class.elements() { - match element { - ClassElement::MethodDefinition(_, method) - | ClassElement::StaticMethodDefinition(_, method) => match method { - MethodDefinition::Get(function) - | MethodDefinition::Set(function) - | MethodDefinition::Ordinary(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - MethodDefinition::Generator(generator) => { - if let Some(Sym::ARGUMENTS) = generator.name() { - return true; - } - } - MethodDefinition::AsyncGenerator(async_generator) => { - if let Some(Sym::ARGUMENTS) = async_generator.name() { - return true; - } - } - MethodDefinition::Async(function) => { - if let Some(Sym::ARGUMENTS) = function.name() { - return true; - } - } - }, - ClassElement::FieldDefinition(_, node) - | ClassElement::StaticFieldDefinition(_, node) - | ClassElement::PrivateFieldDefinition(_, node) - | ClassElement::PrivateStaticFieldDefinition(_, node) => { - if let Some(node) = node { - if node.contains_arguments() { - return true; - } - } - } - ClassElement::StaticBlock(statement_list) => { - for node in statement_list.statements() { - if node.contains_arguments() { - return true; - } - } - } - _ => {} - } - } - } - } - _ => {} + Expression::Identifier(ident) => *ident == Sym::ARGUMENTS, + Expression::Function(_) + | Expression::Generator(_) + | Expression::AsyncFunction(_) + | Expression::AsyncGenerator(_) + | Expression::Literal(_) + | Expression::This + | Expression::NewTarget => false, + Expression::ArrayLiteral(array) => array.contains_arguments(), + Expression::ObjectLiteral(object) => object.contains_arguments(), + Expression::Spread(spread) => spread.contains_arguments(), + Expression::ArrowFunction(arrow) => arrow.contains_arguments(), + Expression::Class(class) => class.contains_arguments(), + Expression::TemplateLiteral(template) => template.contains_arguments(), + Expression::PropertyAccess(access) => access.contains_arguments(), + Expression::SuperPropertyAccess(access) => access.contains_arguments(), + Expression::PrivatePropertyAccess(access) => access.contains_arguments(), + Expression::New(new) => new.contains_arguments(), + Expression::Call(call) => call.contains_arguments(), + Expression::SuperCall(call) => call.contains_arguments(), + Expression::TaggedTemplate(tag) => tag.contains_arguments(), + Expression::Assign(assign) => assign.contains_arguments(), + Expression::Unary(unary) => unary.contains_arguments(), + Expression::Binary(binary) => binary.contains_arguments(), + Expression::Conditional(cond) => cond.contains_arguments(), + Expression::Await(r#await) => r#await.contains_arguments(), + Expression::Yield(r#yield) => r#yield.contains_arguments(), + // TODO: remove variant + Expression::FormalParameterList(_) => unreachable!(), } - false } /// Returns `true` if the node contains the given token. @@ -411,162 +235,41 @@ impl Expression { // TODO: replace with a visitor pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { - Expression::ArrayLiteral(array) => { - for expr in array.as_ref().iter().flatten() { - if expr.contains(symbol) { - return true; - } - } - } - Expression::Assign(assign) => { - match assign.lhs() { - AssignTarget::Property(field) => { - if field.target().contains(symbol) { - return true; - } - match field.field() { - PropertyAccessField::Expr(expr) if expr.contains(symbol) => { - return true - } - _ => {} - } - } - AssignTarget::PrivateProperty(access) => { - if access.target().contains(symbol) { - return true; - } - } - AssignTarget::SuperProperty(_) => { - if symbol == ContainsSymbol::SuperProperty { - return true; - } - } - AssignTarget::Pattern(pattern) => { - if pattern.contains(symbol) { - return true; - } - } - AssignTarget::Identifier(_) => {} - } - if assign.rhs().contains(symbol) { - return true; - } - } - Expression::Await(_) if symbol == ContainsSymbol::AwaitExpression => return true, - Expression::Await(expr) => { - if expr.expr().contains(symbol) { - return true; - } - } - Expression::Binary(bin_op) => { - if bin_op.lhs().contains(symbol) || bin_op.rhs().contains(symbol) { - return true; - } - } - Expression::Call(call) => { - if call.expr().contains(symbol) { - return true; - } - for node in call.args() { - if node.contains(symbol) { - return true; - } - } - } - Expression::New(new) => { - if new.call().expr().contains(symbol) { - return true; - } - for node in new.call().args() { - if node.contains(symbol) { - return true; - } - } - } - Expression::Spread(spread) => { - if spread.val().contains(symbol) { - return true; - } - } - Expression::TaggedTemplate(template) => { - if template.tag().contains(symbol) { - return true; - } - for node in template.exprs() { - if node.contains(symbol) { - return true; - } - } - } - Expression::TemplateLiteral(template) => { - for element in template.elements() { - if let TemplateElement::Expr(node) = element { - if node.contains(symbol) { - return true; - } - } - } - } - Expression::SuperCall(_) if symbol == ContainsSymbol::SuperCall => return true, - Expression::SuperPropertyAccess(_) if symbol == ContainsSymbol::SuperProperty => { - return true - } - Expression::ObjectLiteral(object) => { - for property in object.properties() { - match property { - PropertyDefinition::Property(name, init) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - if init.contains(symbol) { - return true; - } - } - PropertyDefinition::SpreadObject(spread) => { - if spread.contains(symbol) { - return true; - } - } - PropertyDefinition::MethodDefinition(_, name) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - } - PropertyDefinition::IdentifierReference(_) - | PropertyDefinition::CoverInitializedName(_, _) => {} - } - } - } - Expression::Class(class) => { - if let Some(node) = class.super_ref() { - if node.contains(symbol) { - return true; - } - } - for element in class.elements() { - match element { - ClassElement::MethodDefinition(name, _) - | ClassElement::StaticMethodDefinition(name, _) - | ClassElement::FieldDefinition(name, _) - | ClassElement::StaticFieldDefinition(name, _) => { - if let Some(node) = name.computed() { - if node.contains(symbol) { - return true; - } - } - } - _ => {} - } - } + Expression::This => symbol == ContainsSymbol::This, + Expression::Identifier(_) + | Expression::Literal(_) + | Expression::Function(_) + | Expression::Generator(_) + | Expression::AsyncFunction(_) + | Expression::AsyncGenerator(_) => false, + Expression::ArrayLiteral(array) => array.contains(symbol), + Expression::ObjectLiteral(obj) => obj.contains(symbol), + Expression::Spread(spread) => spread.contains(symbol), + Expression::ArrowFunction(arrow) => arrow.contains(symbol), + Expression::Class(class) => class.contains(symbol), + Expression::TemplateLiteral(temp) => temp.contains(symbol), + Expression::PropertyAccess(access) => access.contains(symbol), + Expression::SuperPropertyAccess(_access) if symbol == ContainsSymbol::SuperProperty => { + true } - Expression::Yield(_) if symbol == ContainsSymbol::YieldExpression => return true, - _ => {} + Expression::SuperPropertyAccess(access) => access.contains(symbol), + Expression::PrivatePropertyAccess(access) => access.contains(symbol), + Expression::New(new) => new.contains(symbol), + Expression::Call(call) => call.contains(symbol), + Expression::SuperCall(_) if symbol == ContainsSymbol::SuperCall => true, + Expression::SuperCall(expr) => expr.contains(symbol), + Expression::TaggedTemplate(temp) => temp.contains(symbol), + Expression::NewTarget => symbol == ContainsSymbol::NewTarget, + Expression::Assign(assign) => assign.contains(symbol), + Expression::Unary(unary) => unary.contains(symbol), + Expression::Binary(binary) => binary.contains(symbol), + Expression::Conditional(cond) => cond.contains(symbol), + Expression::Await(_) if symbol == ContainsSymbol::AwaitExpression => true, + Expression::Await(r#await) => r#await.contains(symbol), + Expression::Yield(_) if symbol == ContainsSymbol::YieldExpression => true, + Expression::Yield(r#yield) => r#yield.contains(symbol), + Expression::FormalParameterList(_) => unreachable!(), } - false } } diff --git a/boa_engine/src/syntax/ast/expression/new.rs b/boa_engine/src/syntax/ast/expression/new.rs index 86e576067f1..849faf2654f 100644 --- a/boa_engine/src/syntax/ast/expression/new.rs +++ b/boa_engine/src/syntax/ast/expression/new.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::expression::Call; +use crate::syntax::ast::{expression::Call, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; use super::Expression; @@ -39,6 +39,16 @@ impl New { pub(crate) fn call(&self) -> &Call { &self.call } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.call.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.call.contains(symbol) + } } impl From for New { diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs index 0c2c7de22d8..3547f6e67cb 100644 --- a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs @@ -12,6 +12,7 @@ use crate::syntax::{ Pattern, PatternArray, PatternArrayElement, PatternObject, PatternObjectElement, }, property::{PropertyDefinition, PropertyName}, + ContainsSymbol, }, parser::RESERVED_IDENTIFIERS_STRICT, }; @@ -61,6 +62,28 @@ impl Assign { 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::Property(access) => access.contains_arguments(), + AssignTarget::PrivateProperty(access) => access.contains_arguments(), + AssignTarget::SuperProperty(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::Property(access) => access.contains(symbol), + AssignTarget::PrivateProperty(access) => access.contains(symbol), + AssignTarget::SuperProperty(access) => access.contains(symbol), + AssignTarget::Pattern(pattern) => pattern.contains(symbol), + } || self.rhs.contains(symbol)) + } } impl ToInternedString for Assign { @@ -151,25 +174,25 @@ pub(crate) fn object_decl_to_declaration_pattern( let mut excluded_keys = Vec::new(); for (i, property) in object.properties().iter().enumerate() { match property { - PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { + PropertyDefinition::IdentifierReference(ident) + if strict && ident.sym() == Sym::EVAL => + { return None } PropertyDefinition::IdentifierReference(ident) => { - if strict && RESERVED_IDENTIFIERS_STRICT.contains(ident) { + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) { return None; } excluded_keys.push(*ident); bindings.push(PatternObjectElement::SingleName { ident: *ident, - name: PropertyName::Literal(*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.sym() => - { + (PropertyName::Literal(name), Expression::Identifier(ident)) if *name == *ident => { if strict && *name == Sym::EVAL { return None; } @@ -177,16 +200,16 @@ pub(crate) fn object_decl_to_declaration_pattern( return None; } - excluded_keys.push(*name); + excluded_keys.push(*ident); bindings.push(PatternObjectElement::SingleName { - ident: *name, + ident: *ident, name: PropertyName::Literal(*name), default_init: None, }); } (PropertyName::Literal(name), Expression::Identifier(ident)) => { bindings.push(PatternObjectElement::SingleName { - ident: ident.sym(), + ident: *ident, name: PropertyName::Literal(*name), default_init: None, }); @@ -210,22 +233,22 @@ pub(crate) fn object_decl_to_declaration_pattern( (_, Expression::Assign(assign)) => match assign.lhs() { AssignTarget::Identifier(ident) => { if let Some(name) = name.literal() { - if name == ident.sym() { + if name == *ident { if strict && name == Sym::EVAL { return None; } if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { return None; } - excluded_keys.push(name); + excluded_keys.push(*ident); bindings.push(PatternObjectElement::SingleName { - ident: name, + ident: *ident, name: PropertyName::Literal(name), default_init: Some(assign.rhs().clone()), }); } else { bindings.push(PatternObjectElement::SingleName { - ident: ident.sym(), + ident: *ident, name: PropertyName::Literal(name), default_init: Some(assign.rhs().clone()), }); @@ -261,7 +284,7 @@ pub(crate) fn object_decl_to_declaration_pattern( } (PropertyName::Computed(name), Expression::Identifier(ident)) => { bindings.push(PatternObjectElement::SingleName { - ident: ident.sym(), + ident: *ident, name: PropertyName::Computed(name.clone()), default_init: None, }); @@ -272,7 +295,7 @@ pub(crate) fn object_decl_to_declaration_pattern( match spread { Expression::Identifier(ident) => { bindings.push(PatternObjectElement::RestProperty { - ident: ident.sym(), + ident: *ident, excluded_keys: excluded_keys.clone(), }); } @@ -290,13 +313,13 @@ pub(crate) fn object_decl_to_declaration_pattern( } PropertyDefinition::MethodDefinition(_, _) => return None, PropertyDefinition::CoverInitializedName(ident, expr) => { - if strict && (*ident == Sym::EVAL || *ident == Sym::ARGUMENTS) { + if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) { return None; } bindings.push(PatternObjectElement::SingleName { ident: *ident, - name: PropertyName::Literal(*ident), + name: PropertyName::Literal(ident.sym()), default_init: Some(expr.clone()), }); } @@ -327,19 +350,19 @@ pub(crate) fn array_decl_to_declaration_pattern( }; match expr { Expression::Identifier(ident) => { - if strict && ident.sym() == Sym::ARGUMENTS { + if strict && *ident == Sym::ARGUMENTS { return None; } bindings.push(PatternArrayElement::SingleName { - ident: ident.sym(), + ident: *ident, default_init: None, }); } Expression::Spread(spread) => { match spread.val() { Expression::Identifier(ident) => { - bindings.push(PatternArrayElement::SingleNameRest { ident: ident.sym() }); + bindings.push(PatternArrayElement::SingleNameRest { ident: *ident }); } Expression::PropertyAccess(access) => { bindings.push(PatternArrayElement::PropertyAccessRest { @@ -363,7 +386,7 @@ pub(crate) fn array_decl_to_declaration_pattern( Expression::Assign(assign) => match assign.lhs() { AssignTarget::Identifier(ident) => { bindings.push(PatternArrayElement::SingleName { - ident: ident.sym(), + ident: *ident, default_init: Some(assign.rhs().clone()), }); } diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs index 114426e9208..b045a80a28b 100644 --- a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs @@ -2,7 +2,7 @@ pub mod op; use boa_interner::{Interner, ToInternedString}; -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; /// Binary operations require two operands, one before the operator and one after the operator. /// @@ -42,6 +42,16 @@ impl Binary { pub fn rhs(&self) -> &Expression { &self.rhs } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.lhs.contains_arguments() || self.rhs.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.lhs.contains(symbol) || self.rhs.contains(symbol) + } } impl ToInternedString for Binary { diff --git a/boa_engine/src/syntax/ast/expression/operator/conditional.rs b/boa_engine/src/syntax/ast/expression/operator/conditional.rs index 5d589977d32..886466ff37b 100644 --- a/boa_engine/src/syntax/ast/expression/operator/conditional.rs +++ b/boa_engine/src/syntax/ast/expression/operator/conditional.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; /// The `conditional` (ternary) operation is the only JavaScript operation that takes three @@ -44,6 +44,20 @@ impl Conditional { if_false: Box::new(if_false), } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.condition.contains_arguments() + || self.if_true.contains_arguments() + || self.if_false.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.condition.contains(symbol) + || self.if_true.contains(symbol) + || self.if_false.contains(symbol) + } } impl ToInternedString for Conditional { diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs index 315e55897b3..216ac51fee7 100644 --- a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs @@ -2,7 +2,7 @@ pub mod op; use boa_interner::{Interner, ToInternedString}; -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; /// A unary operation is an operation with only one operand. /// @@ -37,6 +37,16 @@ impl Unary { pub fn target(&self) -> &Expression { self.target.as_ref() } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.target.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.target.contains(symbol) + } } impl ToInternedString for Unary { diff --git a/boa_engine/src/syntax/ast/expression/spread.rs b/boa_engine/src/syntax/ast/expression/spread.rs index e11d9edf2b6..13ce9d83776 100644 --- a/boa_engine/src/syntax/ast/expression/spread.rs +++ b/boa_engine/src/syntax/ast/expression/spread.rs @@ -1,5 +1,7 @@ use boa_interner::{Interner, ToInternedString}; +use crate::syntax::ast::ContainsSymbol; + use super::Expression; /// The `spread` operator allows an iterable such as an array expression or string to be @@ -34,6 +36,16 @@ impl Spread { pub fn new(val: Expression) -> Self { Self { val: Box::new(val) } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.val.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.val.contains(symbol) + } } impl ToInternedString for Spread { diff --git a/boa_engine/src/syntax/ast/expression/tagged_template.rs b/boa_engine/src/syntax/ast/expression/tagged_template.rs index 51f5d52a7db..887dc845dea 100644 --- a/boa_engine/src/syntax/ast/expression/tagged_template.rs +++ b/boa_engine/src/syntax/ast/expression/tagged_template.rs @@ -1,5 +1,7 @@ use boa_interner::{Interner, Sym, ToInternedString}; +use crate::syntax::ast::ContainsSymbol; + use super::Expression; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] @@ -43,6 +45,15 @@ impl TaggedTemplate { pub(crate) fn exprs(&self) -> &[Expression] { &self.exprs } + + pub(crate) fn contains_arguments(&self) -> bool { + self.tag.contains_arguments() || self.exprs.iter().any(Expression::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.tag.contains(symbol) || self.exprs.iter().any(|expr| expr.contains(symbol)) + } } impl ToInternedString for TaggedTemplate { diff --git a/boa_engine/src/syntax/ast/expression/yield.rs b/boa_engine/src/syntax/ast/expression/yield.rs index 837a4535e95..2f028cb7ee8 100644 --- a/boa_engine/src/syntax/ast/expression/yield.rs +++ b/boa_engine/src/syntax/ast/expression/yield.rs @@ -1,5 +1,7 @@ use boa_interner::{Interner, ToInternedString}; +use crate::syntax::ast::ContainsSymbol; + use super::Expression; /// The `yield` keyword is used to pause and resume a generator function @@ -33,6 +35,16 @@ impl Yield { delegate, } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + matches!(self.expr, Some(ref expr) if expr.contains_arguments()) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + matches!(self.expr, Some(ref expr) if expr.contains(symbol)) + } } impl From for Expression { diff --git a/boa_engine/src/syntax/ast/function/arrow_function.rs b/boa_engine/src/syntax/ast/function/arrow_function.rs index f5885b22a8c..cf17696576b 100644 --- a/boa_engine/src/syntax/ast/function/arrow_function.rs +++ b/boa_engine/src/syntax/ast/function/arrow_function.rs @@ -1,7 +1,8 @@ -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::expression::Identifier; use crate::syntax::ast::join_nodes; use crate::syntax::ast::statement::StatementList; -use boa_interner::{Interner, Sym, ToInternedString}; +use crate::syntax::ast::{expression::Expression, ContainsSymbol}; +use boa_interner::{Interner, ToInternedString}; use super::FormalParameterList; @@ -21,7 +22,7 @@ use super::FormalParameterList; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct ArrowFunction { - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, } @@ -29,7 +30,7 @@ pub struct ArrowFunction { impl ArrowFunction { /// Creates a new `ArrowFunctionDecl` AST Expression. pub(in crate::syntax) fn new( - name: Option, + name: Option, params: FormalParameterList, body: StatementList, ) -> Self { @@ -41,12 +42,12 @@ impl ArrowFunction { } /// Gets the name of the function declaration. - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { self.name } /// Sets the name of the function declaration. - pub fn set_name(&mut self, name: Option) { + pub fn set_name(&mut self, name: Option) { self.name = name; } @@ -74,6 +75,26 @@ impl ArrowFunction { } buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.parameters.contains_arguments() || self.body.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + if ![ + ContainsSymbol::NewTarget, + ContainsSymbol::SuperProperty, + ContainsSymbol::SuperCall, + ContainsSymbol::This, + ] + .contains(&symbol) + { + return false; + } + self.parameters.contains(symbol) || self.body.contains(symbol) + } } impl ToInternedString for ArrowFunction { diff --git a/boa_engine/src/syntax/ast/function/async_function.rs b/boa_engine/src/syntax/ast/function/async_function.rs index ea32c6fa43c..30c24f3b3c4 100644 --- a/boa_engine/src/syntax/ast/function/async_function.rs +++ b/boa_engine/src/syntax/ast/function/async_function.rs @@ -1,9 +1,10 @@ //! Async Function Expression. +use crate::syntax::ast::expression::Identifier; use crate::syntax::ast::join_nodes; use crate::syntax::ast::statement::StatementList; use crate::syntax::ast::{expression::Expression, Statement}; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; use super::FormalParameterList; @@ -19,7 +20,7 @@ use super::FormalParameterList; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct AsyncFunction { - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, } @@ -27,7 +28,7 @@ pub struct AsyncFunction { impl AsyncFunction { /// Creates a new function expression pub(in crate::syntax) fn new( - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, ) -> Self { @@ -39,7 +40,7 @@ impl AsyncFunction { } /// Gets the name of the function declaration. - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { self.name } @@ -57,7 +58,7 @@ impl AsyncFunction { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function".to_owned(); if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name))); + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); } buf.push_str(&format!( "({}", diff --git a/boa_engine/src/syntax/ast/function/async_generator.rs b/boa_engine/src/syntax/ast/function/async_generator.rs index 707acd3f4dc..417827f709c 100644 --- a/boa_engine/src/syntax/ast/function/async_generator.rs +++ b/boa_engine/src/syntax/ast/function/async_generator.rs @@ -1,8 +1,8 @@ //! Async Generator Expression -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::expression::{Expression, Identifier}; use crate::syntax::ast::statement::StatementList; use crate::syntax::ast::{block_to_string, join_nodes, Statement}; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; use super::FormalParameterList; @@ -15,7 +15,7 @@ use super::FormalParameterList; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct AsyncGenerator { - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, } @@ -23,7 +23,7 @@ pub struct AsyncGenerator { impl AsyncGenerator { /// Creates a new async generator expression pub(in crate::syntax) fn new( - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, ) -> Self { @@ -35,7 +35,7 @@ impl AsyncGenerator { } /// Gets the name of the async generator expression - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { self.name } @@ -52,7 +52,7 @@ impl AsyncGenerator { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function*".to_owned(); if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name))); + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); } buf.push_str(&format!( "({}) {}", diff --git a/boa_engine/src/syntax/ast/function/class.rs b/boa_engine/src/syntax/ast/function/class.rs index ed0db402fae..b130be9a4a7 100644 --- a/boa_engine/src/syntax/ast/function/class.rs +++ b/boa_engine/src/syntax/ast/function/class.rs @@ -1,10 +1,16 @@ use std::borrow::Cow; -use crate::string::ToStringEscaped; -use crate::syntax::ast::expression::Expression; -use crate::syntax::ast::property::{MethodDefinition, PropertyName}; -use crate::syntax::ast::statement::StatementList; -use crate::syntax::ast::{block_to_string, join_nodes, ContainsSymbol, Statement}; +use crate::{ + string::ToStringEscaped, + syntax::ast::{ + block_to_string, + expression::{Expression, Identifier}, + join_nodes, + property::{MethodDefinition, PropertyName}, + statement::StatementList, + ContainsSymbol, Statement, + }, +}; use boa_interner::{Interner, Sym, ToInternedString}; use super::Function; @@ -22,7 +28,7 @@ use super::Function; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Class { - name: Option, + name: Option, super_ref: Option, constructor: Option, elements: Box<[ClassElement]>, @@ -31,7 +37,7 @@ pub struct Class { impl Class { /// Creates a new class declaration. pub(in crate::syntax) fn new( - name: Option, + name: Option, super_ref: Option, constructor: Option, elements: Box<[ClassElement]>, @@ -45,7 +51,7 @@ impl Class { } /// Returns the name of the class. - pub(crate) fn name(&self) -> Option { + pub(crate) fn name(&self) -> Option { self.name } @@ -64,10 +70,29 @@ impl Class { &self.elements } + pub(crate) fn contains_arguments(&self) -> bool { + matches!(self.name, Some(ref ident) if *ident == Sym::ARGUMENTS) + || matches!(self.super_ref, Some(ref expr) if expr.contains_arguments()) + || self.elements.iter().any(ClassElement::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + if symbol == ContainsSymbol::ClassBody && !self.elements.is_empty() { + return true; + } + if symbol == ContainsSymbol::ClassHeritage { + return self.super_ref.is_some(); + } + + matches!(self.super_ref, Some(ref expr) if expr.contains(symbol)) + || self.elements.iter().any(|elem| elem.contains(symbol)) + } + /// Implements the display formatting with indentation. pub(crate) fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { let class_name = self.name.map_or(Cow::Borrowed(""), |s| { - interner.resolve_expect(s).join( + interner.resolve_expect(s.sym()).join( Cow::Borrowed, |utf16| Cow::Owned(utf16.to_string_escaped()), true, @@ -371,30 +396,19 @@ pub enum ClassElement { } impl ClassElement { + #[inline] pub(crate) fn contains_arguments(&self) -> bool { match self { - Self::MethodDefinition(_, method) | Self::StaticMethodDefinition(_, method) => { - match method { - MethodDefinition::Get(function) - | MethodDefinition::Set(function) - | MethodDefinition::Ordinary(function) => { - matches!(function.name(), Some(Sym::ARGUMENTS)) - } - MethodDefinition::Generator(generator) => { - matches!(generator.name(), Some(Sym::ARGUMENTS)) - } - MethodDefinition::AsyncGenerator(async_generator) => { - matches!(async_generator.name(), Some(Sym::ARGUMENTS)) - } - MethodDefinition::Async(function) => { - matches!(function.name(), Some(Sym::ARGUMENTS)) - } - } + // Skipping function since they must not have names + Self::MethodDefinition(name, _) | Self::StaticMethodDefinition(name, _) => { + name.contains_arguments() } - Self::FieldDefinition(_, Some(node)) - | Self::StaticFieldDefinition(_, Some(node)) - | Self::PrivateFieldDefinition(_, Some(node)) - | Self::PrivateStaticFieldDefinition(_, Some(node)) => node.contains_arguments(), + Self::FieldDefinition(name, Some(init)) + | Self::StaticFieldDefinition(name, Some(init)) => { + name.contains_arguments() || init.contains_arguments() + } + Self::PrivateFieldDefinition(_, Some(init)) + | Self::PrivateStaticFieldDefinition(_, Some(init)) => init.contains_arguments(), Self::StaticBlock(statement_list) => statement_list .statements() .iter() @@ -403,14 +417,20 @@ impl ClassElement { } } + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { - Self::MethodDefinition(name, _) - | Self::StaticMethodDefinition(name, _) - | Self::FieldDefinition(name, _) - | Self::StaticFieldDefinition(name, _) => { - matches!(name.computed(), Some(expr) if expr.contains(symbol)) + // Skipping function since they must not have names + Self::MethodDefinition(name, _) | Self::StaticMethodDefinition(name, _) => { + name.contains(symbol) + } + Self::FieldDefinition(name, Some(init)) + | Self::StaticFieldDefinition(name, Some(init)) => { + name.contains(symbol) || init.contains(symbol) } + Self::PrivateFieldDefinition(_, Some(init)) + | Self::PrivateStaticFieldDefinition(_, Some(init)) => init.contains(symbol), + Self::StaticBlock(_statement_list) => false, _ => false, } } diff --git a/boa_engine/src/syntax/ast/function/generator.rs b/boa_engine/src/syntax/ast/function/generator.rs index ad58b1d4521..9836745f26d 100644 --- a/boa_engine/src/syntax/ast/function/generator.rs +++ b/boa_engine/src/syntax/ast/function/generator.rs @@ -1,8 +1,8 @@ -use crate::syntax::ast::expression::Expression; +use crate::syntax::ast::expression::{Expression, Identifier}; use crate::syntax::ast::{block_to_string, join_nodes, Statement}; use crate::syntax::ast::statement::StatementList; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; use super::FormalParameterList; @@ -17,7 +17,7 @@ use super::FormalParameterList; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Generator { - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, } @@ -25,7 +25,7 @@ pub struct Generator { impl Generator { /// Creates a new generator expression pub(in crate::syntax) fn new( - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, ) -> Self { @@ -37,7 +37,7 @@ impl Generator { } /// Gets the name of the generator declaration. - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { self.name } @@ -55,7 +55,7 @@ impl Generator { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "function*".to_owned(); if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name))); + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); } buf.push_str(&format!( "({}) {}", diff --git a/boa_engine/src/syntax/ast/function/mod.rs b/boa_engine/src/syntax/ast/function/mod.rs index a412646c359..89bdfe1920a 100644 --- a/boa_engine/src/syntax/ast/function/mod.rs +++ b/boa_engine/src/syntax/ast/function/mod.rs @@ -16,9 +16,9 @@ pub(crate) use parameters::FormalParameterListFlags; use crate::syntax::ast::statement::StatementList; use crate::syntax::ast::{block_to_string, join_nodes}; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; -use super::expression::Expression; +use super::expression::{Expression, Identifier}; use super::{ContainsSymbol, Statement}; /// The `function` expression defines a function with the specified parameters. @@ -40,7 +40,7 @@ use super::{ContainsSymbol, Statement}; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub struct Function { - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, } @@ -48,7 +48,7 @@ pub struct Function { impl Function { /// Creates a new function expression pub(in crate::syntax) fn new( - name: Option, + name: Option, parameters: FormalParameterList, body: StatementList, ) -> Self { @@ -60,7 +60,7 @@ impl Function { } /// Gets the name of the function declaration. - pub fn name(&self) -> Option { + pub fn name(&self) -> Option { self.name } @@ -78,7 +78,7 @@ impl Function { pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "function".to_owned(); if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name))); + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); } buf.push_str(&format!( "({}) {}", diff --git a/boa_engine/src/syntax/ast/function/parameters.rs b/boa_engine/src/syntax/ast/function/parameters.rs index 8a127c7260f..5997587b392 100644 --- a/boa_engine/src/syntax/ast/function/parameters.rs +++ b/boa_engine/src/syntax/ast/function/parameters.rs @@ -1,6 +1,6 @@ use crate::syntax::{ ast::{ - expression::Expression, + expression::{Expression, Identifier}, pattern::Pattern, statement::declaration::{Binding, Declaration}, ContainsSymbol, Position, @@ -85,7 +85,7 @@ impl FormalParameterList { /// Helper to check if any parameter names are declared in the given list. pub(crate) fn name_in_lexically_declared_names( &self, - names: &[Sym], + names: &[Identifier], position: Position, ) -> Result<(), ParseError> { for parameter in self.parameters.iter() { @@ -126,6 +126,18 @@ impl FormalParameterList { } false } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.parameters + .iter() + .any(FormalParameter::contains_arguments) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.parameters.iter().any(|param| param.contains(symbol)) + } } impl From> for FormalParameterList { @@ -183,7 +195,7 @@ impl From for FormalParameterList { if parameter.init().is_some() { flags |= FormalParameterListFlags::HAS_EXPRESSIONS; } - if parameter.names().contains(&Sym::ARGUMENTS) { + if parameter.names().contains(&Sym::ARGUMENTS.into()) { flags |= FormalParameterListFlags::HAS_ARGUMENTS; } if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() { @@ -256,9 +268,9 @@ impl FormalParameter { } /// Gets the name of the formal parameter. - pub fn names(&self) -> Vec { + pub fn names(&self) -> Vec { match self.declaration.binding() { - Binding::Identifier(ident) => vec![ident.sym()], + Binding::Identifier(ident) => vec![*ident], Binding::Pattern(pattern) => match pattern { Pattern::Object(object_pattern) => object_pattern.idents(), @@ -285,6 +297,15 @@ impl FormalParameter { pub fn is_identifier(&self) -> bool { matches!(&self.declaration.binding(), Binding::Identifier(_)) } + + pub(crate) fn contains_arguments(&self) -> bool { + self.declaration.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.declaration.contains(symbol) + } } impl ToInternedString for FormalParameter { diff --git a/boa_engine/src/syntax/ast/mod.rs b/boa_engine/src/syntax/ast/mod.rs index af028ebd52b..f26b6cf4599 100644 --- a/boa_engine/src/syntax/ast/mod.rs +++ b/boa_engine/src/syntax/ast/mod.rs @@ -28,6 +28,10 @@ pub(crate) enum ContainsSymbol { YieldExpression, AwaitExpression, NewTarget, + ClassBody, + ClassHeritage, + This, + MethodDefinition, } /// Utility to join multiple Nodes into a single string. diff --git a/boa_engine/src/syntax/ast/pattern.rs b/boa_engine/src/syntax/ast/pattern.rs index e1df60aa585..180c53ae6f2 100644 --- a/boa_engine/src/syntax/ast/pattern.rs +++ b/boa_engine/src/syntax/ast/pattern.rs @@ -1,7 +1,10 @@ -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; use super::{ - expression::access::{PropertyAccess, PropertyAccessField}, + expression::{ + access::{PropertyAccess, PropertyAccessField}, + Identifier, + }, property::PropertyName, ContainsSymbol, Expression, }; @@ -58,7 +61,7 @@ impl Pattern { /// /// A single pattern may have 0 to n identifiers. #[inline] - pub fn idents(&self) -> Vec { + pub fn idents(&self) -> Vec { match &self { Pattern::Object(pattern) => pattern.idents(), Pattern::Array(pattern) => pattern.idents(), @@ -74,7 +77,7 @@ impl Pattern { #[inline] pub(crate) fn contains_arguments(&self) -> bool { match self { - Pattern::Object(pattern) => pattern.contains_arguments(), + Pattern::Object(object) => object.contains_arguments(), Pattern::Array(array) => array.contains_arguments(), } } @@ -85,6 +88,7 @@ impl Pattern { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { Pattern::Object(object) => object.contains(symbol), @@ -168,13 +172,14 @@ impl PatternObject { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + #[inline] 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 { + pub(crate) fn idents(&self) -> Vec { self.0 .iter() .flat_map(PatternObjectElement::idents) @@ -256,7 +261,7 @@ impl PatternArray { /// Gets the list of identifiers declared by the array binding pattern. #[inline] - pub(crate) fn idents(&self) -> Vec { + pub(crate) fn idents(&self) -> Vec { self.0 .iter() .flat_map(PatternArrayElement::idents) @@ -289,7 +294,7 @@ pub enum PatternObjectElement { /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty SingleName { name: PropertyName, - ident: Sym, + ident: Identifier, default_init: Option, }, @@ -302,7 +307,10 @@ pub enum PatternObjectElement { /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] /// /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty - RestProperty { ident: Sym, excluded_keys: Vec }, + RestProperty { + ident: Identifier, + excluded_keys: Vec, + }, /// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. /// @@ -330,7 +338,7 @@ pub enum PatternObjectElement { /// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty AssignmentRestPropertyAccess { access: PropertyAccess, - excluded_keys: Vec, + excluded_keys: Vec, }, /// Pattern represents a property with a `Pattern` as the element. @@ -444,7 +452,7 @@ impl PatternObjectElement { /// Gets the list of identifiers declared by the object binding pattern. #[inline] - pub(crate) fn idents(&self) -> Vec { + pub(crate) fn idents(&self) -> Vec { match self { Self::SingleName { ident, .. } | Self::RestProperty { ident, .. } => { vec![*ident] @@ -453,7 +461,7 @@ impl PatternObjectElement { name: PropertyName::Literal(lit), .. } => { - vec![*lit] + vec![(*lit).into()] } Self::Pattern { pattern, .. } => pattern.idents(), _ => Vec::new(), @@ -467,25 +475,25 @@ impl ToInternedString for PatternObjectElement { Self::Empty => String::new(), Self::SingleName { ident, - name: property_name, + name, default_init, } => { - let mut buf = match property_name { - PropertyName::Literal(name) if *name == *ident => { - format!(" {}", interner.resolve_expect(*ident)) + let mut buf = match name { + PropertyName::Literal(name) if name == ident => { + format!(" {}", interner.resolve_expect(ident.sym())) } PropertyName::Literal(name) => { format!( " {} : {}", interner.resolve_expect(*name), - interner.resolve_expect(*ident) + interner.resolve_expect(ident.sym()) ) } PropertyName::Computed(node) => { format!( " [{}] : {}", node.to_interned_string(interner), - interner.resolve_expect(*ident) + interner.resolve_expect(ident.sym()) ) } }; @@ -495,10 +503,10 @@ impl ToInternedString for PatternObjectElement { buf } Self::RestProperty { - ident: property_name, + ident, excluded_keys: _, } => { - format!(" ... {}", interner.resolve_expect(*property_name)) + format!(" ... {}", interner.resolve_expect(ident.sym())) } Self::AssignmentRestPropertyAccess { access, .. } => { format!(" ... {}", access.to_interned_string(interner)) @@ -597,7 +605,7 @@ pub enum PatternArrayElement { /// /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding SingleName { - ident: Sym, + ident: Identifier, default_init: Option, }, @@ -631,7 +639,7 @@ pub enum PatternArrayElement { /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] /// /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement - SingleNameRest { ident: Sym }, + SingleNameRest { ident: Identifier }, /// PropertyAccess represents a rest (spread operator) with a property accessor. /// @@ -757,7 +765,7 @@ impl PatternArrayElement { /// Gets the list of identifiers in the array pattern element. #[inline] - pub(crate) fn idents(&self) -> Vec { + pub(crate) fn idents(&self) -> Vec { match self { Self::SingleName { ident, .. } => { vec![*ident] @@ -778,7 +786,7 @@ impl ToInternedString for PatternArrayElement { ident, default_init, } => { - let mut buf = format!(" {}", interner.resolve_expect(*ident)); + let mut buf = format!(" {}", interner.resolve_expect(ident.sym())); if let Some(ref init) = default_init { buf.push_str(&format!(" = {}", init.to_interned_string(interner))); } @@ -798,7 +806,7 @@ impl ToInternedString for PatternArrayElement { buf } Self::SingleNameRest { ident } => { - format!(" ... {}", interner.resolve_expect(*ident)) + format!(" ... {}", interner.resolve_expect(ident.sym())) } Self::PropertyAccessRest { access } => { format!(" ... {}", access.to_interned_string(interner)) diff --git a/boa_engine/src/syntax/ast/property.rs b/boa_engine/src/syntax/ast/property.rs index f416b7ec043..6148a72fdff 100644 --- a/boa_engine/src/syntax/ast/property.rs +++ b/boa_engine/src/syntax/ast/property.rs @@ -1,12 +1,10 @@ use boa_interner::{Interner, Sym, ToInternedString}; -use crate::string::ToStringEscaped; - use super::{ - expression::literal::Literal, + expression::{literal::Literal, Identifier}, function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, statement::StatementList, - Expression, + ContainsSymbol, Expression, }; /// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure. @@ -33,7 +31,7 @@ pub enum PropertyDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions - IdentifierReference(Sym), + IdentifierReference(Identifier), /// Binds a property name to a JavaScript value. /// @@ -53,7 +51,7 @@ pub enum PropertyDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions - MethodDefinition(MethodDefinition, PropertyName), + MethodDefinition(PropertyName, MethodDefinition), /// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. /// It copies own enumerable properties from a provided object onto a new object. @@ -74,7 +72,44 @@ pub enum PropertyDefinition { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName - CoverInitializedName(Sym, Expression), + CoverInitializedName(Identifier, Expression), +} + +impl PropertyDefinition { + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + PropertyDefinition::IdentifierReference(ident) => *ident == Sym::ARGUMENTS, + PropertyDefinition::Property(name, expr) => { + name.contains_arguments() || expr.contains_arguments() + } + // Skipping definition since functions are excluded from the search + PropertyDefinition::MethodDefinition(name, _) => name.contains_arguments(), + PropertyDefinition::SpreadObject(expr) => expr.contains_arguments(), + PropertyDefinition::CoverInitializedName(ident, expr) => { + *ident == Sym::ARGUMENTS || expr.contains_arguments() + } + } + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + PropertyDefinition::IdentifierReference(_) => false, + PropertyDefinition::Property(name, expr) => { + name.contains(symbol) || expr.contains(symbol) + } + // Skipping definition since functions are excluded from the search + PropertyDefinition::MethodDefinition(_, _) + if symbol == ContainsSymbol::MethodDefinition => + { + true + } + PropertyDefinition::MethodDefinition(name, _) => name.contains(symbol), + PropertyDefinition::SpreadObject(expr) => expr.contains(symbol), + PropertyDefinition::CoverInitializedName(_ident, expr) => expr.contains(symbol), + } + } } /// Method definition. @@ -242,16 +277,28 @@ impl PropertyName { PropertyName::Computed(_) => None, } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + PropertyName::Literal(_) => false, + PropertyName::Computed(expr) => expr.contains_arguments(), + } + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + PropertyName::Literal(_) => false, + PropertyName::Computed(expr) => expr.contains(symbol), + } + } } impl ToInternedString for PropertyName { fn to_interned_string(&self, interner: &Interner) -> String { match self { - PropertyName::Literal(key) => interner.resolve_expect(*key).join( - String::from, - ToStringEscaped::to_string_escaped, - true, - ), + PropertyName::Literal(key) => interner.resolve_expect(*key).to_string(), PropertyName::Computed(key) => key.to_interned_string(interner), } } diff --git a/boa_engine/src/syntax/ast/statement/block.rs b/boa_engine/src/syntax/ast/statement/block.rs index be70289f457..bc6ed029b1d 100644 --- a/boa_engine/src/syntax/ast/statement/block.rs +++ b/boa_engine/src/syntax/ast/statement/block.rs @@ -1,5 +1,7 @@ //! Block AST node. +use crate::syntax::ast::{expression::Identifier, ContainsSymbol}; + use super::{Statement, StatementList}; use boa_interner::{Interner, Sym, ToInternedString}; @@ -33,7 +35,7 @@ impl Block { } /// Get the lexically declared names of the block. - pub(crate) fn lexically_declared_names(&self) -> Vec<(Sym, bool)> { + pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { self.statements.lexically_declared_names() } @@ -54,6 +56,16 @@ impl Block { pub fn set_label(&mut self, label: Sym) { self.label = Some(label); } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.statements.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.statements.contains(symbol) + } } impl From for Block diff --git a/boa_engine/src/syntax/ast/statement/declaration.rs b/boa_engine/src/syntax/ast/statement/declaration.rs index f4e5573dc75..0fe72c61171 100644 --- a/boa_engine/src/syntax/ast/statement/declaration.rs +++ b/boa_engine/src/syntax/ast/statement/declaration.rs @@ -6,7 +6,7 @@ use crate::syntax::ast::{ statement::Statement, ContainsSymbol, }; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] @@ -72,13 +72,14 @@ pub enum DeclarationList { } impl DeclarationList { - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.as_ref().iter().any(|decl| decl.contains(symbol)) - } - + #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.as_ref().iter().any(Declaration::contains_arguments) } + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.as_ref().iter().any(|decl| decl.contains(symbol)) + } } impl AsRef<[Declaration]> for DeclarationList { @@ -180,6 +181,7 @@ impl Declaration { self.init.as_ref() } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { if let Some(ref node) = self.init { if node.contains_arguments() { @@ -195,6 +197,7 @@ impl Declaration { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { if let Some(ref node) = self.init { if node.contains(symbol) { @@ -205,7 +208,7 @@ impl Declaration { } /// Gets the list of declared identifiers. - pub(crate) fn idents(&self) -> Vec { + pub(crate) fn idents(&self) -> Vec { self.binding.idents() } } @@ -251,9 +254,9 @@ impl Binding { } /// Gets the list of declared identifiers. - pub(crate) fn idents(&self) -> Vec { + pub(crate) fn idents(&self) -> Vec { match self { - Binding::Identifier(id) => vec![id.sym()], + Binding::Identifier(id) => vec![*id], Binding::Pattern(ref pat) => pat.idents(), } } diff --git a/boa_engine/src/syntax/ast/statement/if.rs b/boa_engine/src/syntax/ast/statement/if.rs index 3dfc9a13900..567153e2bd7 100644 --- a/boa_engine/src/syntax/ast/statement/if.rs +++ b/boa_engine/src/syntax/ast/statement/if.rs @@ -1,6 +1,6 @@ //! If statement -use crate::syntax::ast::{expression::Expression, statement::Statement}; +use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, ToInternedString}; /// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If @@ -65,6 +65,20 @@ impl If { } buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.condition.contains_arguments() + || self.body.contains_arguments() + || matches!(self.else_node, Some(ref stmt) if stmt.contains_arguments()) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.condition.contains(symbol) + || self.body.contains(symbol) + || matches!(self.else_node, Some(ref stmt) if stmt.contains(symbol)) + } } impl ToInternedString for If { diff --git a/boa_engine/src/syntax/ast/statement/iteration/break.rs b/boa_engine/src/syntax/ast/statement/iteration/break.rs index 995a60f921d..57dba8e8dee 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/break.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/break.rs @@ -31,6 +31,11 @@ impl Break { pub fn label(&self) -> Option { self.label } + + #[inline] + pub(crate) fn contains_arguments(self) -> bool { + matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } } impl ToInternedString for Break { diff --git a/boa_engine/src/syntax/ast/statement/iteration/continue.rs b/boa_engine/src/syntax/ast/statement/iteration/continue.rs index 5718c5f495d..ed5709294f9 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/continue.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/continue.rs @@ -28,6 +28,11 @@ impl Continue { pub fn label(&self) -> Option { self.label } + + #[inline] + pub(crate) fn contains_arguments(self) -> bool { + matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } } impl ToInternedString for Continue { diff --git a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs index db8229e17b7..a657e57081c 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{expression::Expression, statement::Statement}; +use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, Sym, ToInternedString}; /// The `do...while` statement creates a loop that executes a specified statement until the @@ -62,6 +62,18 @@ impl DoWhileLoop { buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.body.contains_arguments() + || self.condition.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.body.contains(symbol) || self.condition.contains(symbol) + } } impl ToInternedString for DoWhileLoop { diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs index ebf234e1153..dfacdcc7633 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs @@ -1,6 +1,7 @@ use crate::syntax::ast::{ expression::Expression, statement::{iteration::IterableLoopInitializer, Statement}, + ContainsSymbol, }; use boa_interner::{Interner, Sym, ToInternedString}; #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] @@ -58,6 +59,19 @@ impl ForInLoop { buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.init.contains_arguments() + || self.expr.contains_arguments() + || self.body.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.init.contains(symbol) || self.expr.contains(symbol) || self.body.contains(symbol) + } } impl ToInternedString for ForInLoop { diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs index 5d59dea1435..0f2dfd4c140 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs @@ -1,4 +1,5 @@ use crate::syntax::ast::{ + expression::Identifier, statement::{ declaration::{Declaration, DeclarationList}, Statement, @@ -94,6 +95,25 @@ impl ForLoop { pub fn set_label(&mut self, label: Sym) { self.label = Some(label); } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + let inner = &self.inner; + matches!(inner.init, Some(ref init) if init.contains_arguments()) + || matches!(inner.condition, Some(ref expr) if expr.contains_arguments()) + || matches!(inner.final_expr, Some(ref expr) if expr.contains_arguments()) + || inner.body.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + let inner = &self.inner; + matches!(inner.init, Some(ref init) if init.contains(symbol)) + || matches!(inner.condition, Some(ref expr) if expr.contains(symbol)) + || matches!(inner.final_expr, Some(ref expr) if expr.contains(symbol)) + || inner.body.contains(symbol) + } } impl ToInternedString for ForLoop { @@ -169,7 +189,7 @@ impl ForLoopInitializer { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames - pub(crate) fn bound_names(&self) -> Vec { + pub(crate) fn bound_names(&self) -> Vec { match self { Self::DeclarationList(DeclarationList::Let(list) | DeclarationList::Const(list)) => { list.iter().flat_map(Declaration::idents).collect() @@ -178,19 +198,20 @@ impl ForLoopInitializer { } } - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - match self { - Self::DeclarationList(list) => list.contains(symbol), - Self::Expression(expr) => expr.contains(symbol), - } - } - + #[inline] pub(crate) fn contains_arguments(&self) -> bool { match self { Self::DeclarationList(list) => list.contains_arguments(), Self::Expression(expr) => expr.contains_arguments(), } } + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + match self { + Self::DeclarationList(list) => list.contains(symbol), + Self::Expression(expr) => expr.contains(symbol), + } + } } impl ToInternedString for ForLoopInitializer { diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs index 3cef364d8da..5fb4c31ca3a 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs @@ -1,6 +1,7 @@ use crate::syntax::ast::{ expression::Expression, statement::{iteration::IterableLoopInitializer, Statement}, + ContainsSymbol, }; use boa_interner::{Interner, Sym, ToInternedString}; @@ -75,6 +76,19 @@ impl ForOfLoop { buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.init.contains_arguments() + || self.iterable.contains_arguments() + || self.body.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.init.contains(symbol) || self.iterable.contains(symbol) || self.body.contains(symbol) + } } impl ToInternedString for ForOfLoop { diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/mod.rs index 27d57d1e8a2..9d262684327 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/mod.rs @@ -40,13 +40,22 @@ impl IterableLoopInitializer { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames - pub(crate) fn bound_names(&self) -> Vec { + pub(crate) fn bound_names(&self) -> Vec { match self { Self::Let(binding) | Self::Const(binding) => binding.idents(), _ => Vec::new(), } } + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + match self { + Self::Identifier(ident) => *ident == Sym::ARGUMENTS, + Self::Var(bind) | Self::Let(bind) | Self::Const(bind) => bind.contains_arguments(), + } + } + + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { Self::Var(declaration) | Self::Let(declaration) | Self::Const(declaration) => { @@ -55,13 +64,6 @@ impl IterableLoopInitializer { Self::Identifier(_) => false, } } - - pub(crate) fn contains_arguments(&self) -> bool { - match self { - Self::Identifier(ident) => ident.sym() == Sym::ARGUMENTS, - Self::Var(bind) | Self::Let(bind) | Self::Const(bind) => bind.contains_arguments(), - } - } } impl ToInternedString for IterableLoopInitializer { diff --git a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs b/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs index b7efe78271d..97699428c67 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{expression::Expression, statement::Statement}; +use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol}; use boa_interner::{Interner, Sym, ToInternedString}; /// The `while` statement creates a loop that executes a specified statement as long as the /// test condition evaluates to `true`. @@ -60,6 +60,17 @@ impl WhileLoop { buf } + + pub(crate) fn contains_arguments(&self) -> bool { + self.condition.contains_arguments() + || self.body.contains_arguments() + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.condition.contains(symbol) || self.body.contains(symbol) + } } impl ToInternedString for WhileLoop { diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs index 768b6242f35..18db6375979 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/boa_engine/src/syntax/ast/statement/mod.rs @@ -22,17 +22,17 @@ pub use self::{ throw::Throw, }; use self::{ - declaration::{Binding, Declaration, DeclarationList}, + declaration::{Binding, DeclarationList}, iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, }; -use boa_interner::{Interner, Sym, ToInternedString}; +use boa_interner::{Interner, ToInternedString}; use rustc_hash::FxHashSet; use std::cmp::Ordering; use super::{ - expression::Expression, - function::{AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator}, + expression::{Expression, Identifier}, + function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}, ContainsSymbol, }; @@ -179,7 +179,7 @@ impl Statement { } } - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { match self { Statement::DeclarationList(DeclarationList::Var(list)) => { for decl in &**list { @@ -211,7 +211,7 @@ impl Statement { for declaration in declarations.iter() { match declaration.binding() { Binding::Identifier(ident) => { - vars.insert(ident.sym()); + vars.insert(*ident); } Binding::Pattern(pattern) => { for ident in pattern.idents() { @@ -273,60 +273,27 @@ impl Statement { // TODO: replace with a visitor pub(crate) fn contains_arguments(&self) -> bool { match self { + Self::Function(_) + | Self::Generator(_) + | Self::AsyncGenerator(_) + | Self::AsyncFunction(_) + | Self::Empty => false, + Statement::Block(block) => block.contains_arguments(), + Statement::DeclarationList(decl) => decl.contains_arguments(), Statement::Expression(expr) => expr.contains_arguments(), - - Statement::Block(block) => block.statements().iter().any(Statement::contains_arguments), - Statement::DoWhileLoop(do_while_loop) => { - do_while_loop.body().contains_arguments() - || do_while_loop.cond().contains_arguments() - } - Statement::ForLoop(for_loop) => { - matches!(for_loop.init(), Some(expr) if expr.contains_arguments()) - || matches!(for_loop.condition(), Some(expr) if expr.contains_arguments()) - || matches!(for_loop.final_expr(), Some(expr) if expr.contains_arguments()) - || for_loop.body().contains_arguments() - } - Statement::ForInLoop(for_in_loop) => { - for_in_loop.init().contains_arguments() - || for_in_loop.expr().contains_arguments() - || for_in_loop.body().contains_arguments() - } - Statement::ForOfLoop(for_of_loop) => { - for_of_loop.init().contains_arguments() - || for_of_loop.iterable().contains_arguments() - || for_of_loop.body().contains_arguments() - } - Statement::If(r#if) => { - r#if.cond().contains_arguments() - || r#if.body().contains_arguments() - || matches!(r#if.else_node(), Some(node) if node.contains_arguments()) - } - - Statement::DeclarationList(decl_list) => decl_list - .as_ref() - .iter() - .any(Declaration::contains_arguments), - Statement::Return(r#return) => { - matches!(r#return.expr(), Some(expr) if expr.contains_arguments()) - } - Statement::Switch(switch) => { - switch.val().contains_arguments() - || switch.cases().iter().any(Case::contains_arguments) - } - Statement::Throw(throw) => throw.expr().contains_arguments(), + Statement::If(r#if) => r#if.contains_arguments(), + Statement::DoWhileLoop(dowhile) => dowhile.contains_arguments(), + Statement::WhileLoop(whileloop) => whileloop.contains_arguments(), + Statement::ForLoop(forloop) => forloop.contains_arguments(), + Statement::ForInLoop(forin) => forin.contains_arguments(), + Statement::ForOfLoop(forof) => forof.contains_arguments(), + Statement::Switch(switch) => switch.contains_arguments(), + Statement::Continue(r#continue) => r#continue.contains_arguments(), + Statement::Break(r#break) => r#break.contains_arguments(), + Statement::Return(r#return) => r#return.contains_arguments(), + Statement::Throw(throw) => throw.contains_arguments(), Statement::Try(r#try) => r#try.contains_arguments(), - Statement::WhileLoop(while_loop) => { - while_loop.condition().contains_arguments() - || while_loop.body().contains_arguments() - } - Statement::Class(class) => { - matches!(class.super_ref(), Some(expr) if expr.contains_arguments()) - || class - .elements() - .iter() - .any(ClassElement::contains_arguments) - } - _ => false, + Statement::Class(class) => class.contains_arguments(), } } @@ -339,54 +306,27 @@ impl Statement { // TODO: replace with a visitor pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { match self { + Self::Function(_) + | Self::Generator(_) + | Self::AsyncGenerator(_) + | Self::AsyncFunction(_) + | Self::Empty + | Self::Continue(_) + | Self::Break(_) => false, + Statement::Block(block) => block.contains(symbol), + Statement::DeclarationList(decl) => decl.contains(symbol), Statement::Expression(expr) => expr.contains(symbol), - - Statement::Block(block) => block.statements().iter().any(|stmt| stmt.contains(symbol)), - Statement::DoWhileLoop(do_while_loop) => { - do_while_loop.body().contains(symbol) || do_while_loop.cond().contains(symbol) - } - Statement::ForLoop(for_loop) => { - matches!(for_loop.init(), Some(expr) if expr.contains(symbol)) - || matches!(for_loop.condition(), Some(expr) if expr.contains(symbol)) - || matches!(for_loop.final_expr(), Some(expr) if expr.contains(symbol)) - || for_loop.body().contains(symbol) - } - Statement::ForInLoop(for_in_loop) => { - for_in_loop.init().contains(symbol) - || for_in_loop.expr().contains(symbol) - || for_in_loop.body().contains(symbol) - } - Statement::ForOfLoop(for_of_loop) => { - for_of_loop.init().contains(symbol) - || for_of_loop.iterable().contains(symbol) - || for_of_loop.body().contains(symbol) - } - Statement::If(r#if) => { - r#if.cond().contains(symbol) - || r#if.body().contains(symbol) - || matches!(r#if.else_node(), Some(expr) if expr.contains(symbol)) - } - - Statement::DeclarationList(decl_list) => { - decl_list.as_ref().iter().any(|decl| decl.contains(symbol)) - } - Statement::Return(r#return) => { - matches!(r#return.expr(), Some(expr) if expr.contains(symbol)) - } - Statement::Switch(switch) => { - switch.val().contains(symbol) - || switch.cases().iter().any(|case| case.contains(symbol)) - } - Statement::Throw(throw) => throw.expr().contains(symbol), + Statement::If(r#if) => r#if.contains(symbol), + Statement::DoWhileLoop(dowhile) => dowhile.contains(symbol), + Statement::WhileLoop(whileloop) => whileloop.contains(symbol), + Statement::ForLoop(forloop) => forloop.contains(symbol), + Statement::ForInLoop(forin) => forin.contains(symbol), + Statement::ForOfLoop(forof) => forof.contains(symbol), + Statement::Switch(switch) => switch.contains(symbol), + Statement::Return(r#return) => r#return.contains(symbol), + Statement::Throw(throw) => throw.contains(symbol), Statement::Try(r#try) => r#try.contains(symbol), - Statement::WhileLoop(while_loop) => { - while_loop.condition().contains(symbol) || while_loop.body().contains(symbol) - } - Statement::Class(class) => { - matches!(class.super_ref(), Some(expr) if expr.contains(symbol)) - || class.elements().iter().any(|elem| elem.contains(symbol)) - } - _ => false, + Statement::Class(class) => class.contains(symbol), } } } diff --git a/boa_engine/src/syntax/ast/statement/return.rs b/boa_engine/src/syntax/ast/statement/return.rs index 8e08b51e524..63596f4530d 100644 --- a/boa_engine/src/syntax/ast/statement/return.rs +++ b/boa_engine/src/syntax/ast/statement/return.rs @@ -1,4 +1,8 @@ -use crate::syntax::ast::{expression::Expression, statement::Statement}; +use crate::syntax::ast::{ + expression::{Expression, Identifier}, + statement::Statement, + ContainsSymbol, +}; use boa_interner::{Interner, Sym, ToInternedString}; /// The `return` statement ends function execution and specifies a value to be returned to the @@ -23,11 +27,11 @@ use boa_interner::{Interner, Sym, ToInternedString}; #[derive(Clone, Debug, PartialEq)] pub struct Return { expr: Option, - label: Option, + label: Option, } impl Return { - pub fn label(&self) -> Option { + pub fn label(&self) -> Option { self.label } @@ -36,9 +40,19 @@ impl Return { } /// Creates a `Return` AST node. - pub fn new(expr: Option, label: Option) -> Self { + pub fn new(expr: Option, label: Option) -> Self { Self { expr, label } } + + pub(crate) fn contains_arguments(&self) -> bool { + matches!(self.expr, Some(ref expr) if expr.contains_arguments()) + || matches!(self.label, Some(label) if label == Sym::ARGUMENTS) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + matches!(self.expr, Some(ref expr) if expr.contains(symbol)) + } } impl From for Statement { diff --git a/boa_engine/src/syntax/ast/statement/statement_list/mod.rs b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs index 2cb2bc1ca81..5a15960e9a2 100644 --- a/boa_engine/src/syntax/ast/statement/statement_list/mod.rs +++ b/boa_engine/src/syntax/ast/statement/statement_list/mod.rs @@ -1,7 +1,7 @@ //! Statement list node. -use crate::syntax::ast::{statement::Statement, ContainsSymbol}; -use boa_interner::{Interner, Sym, ToInternedString}; +use crate::syntax::ast::{expression::Identifier, statement::Statement, ContainsSymbol}; +use boa_interner::{Interner, ToInternedString}; use rustc_hash::FxHashSet; @@ -69,7 +69,7 @@ impl StatementList { buf } - pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { + pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) { for stmt in &*self.statements { stmt.var_declared_names(vars); } @@ -85,7 +85,7 @@ impl StatementList { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames - pub(crate) fn lexically_declared_names(&self) -> Vec<(Sym, bool)> { + pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> { let mut names = Vec::new(); for node in self.statements() { @@ -121,7 +121,7 @@ impl StatementList { for decl in declarations.iter() { match decl.binding() { Binding::Identifier(ident) => { - names.push((ident.sym(), false)); + names.push((*ident, false)); } Binding::Pattern(pattern) => { names.extend( @@ -149,7 +149,7 @@ impl StatementList { /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames - pub(crate) fn lexically_declared_names_top_level(&self) -> Vec { + pub(crate) fn lexically_declared_names_top_level(&self) -> Vec { let mut names = Vec::new(); for node in self.statements() { @@ -165,7 +165,7 @@ impl StatementList { for decl in &**declarations { match decl.binding() { Binding::Identifier(ident) => { - names.push(ident.sym()); + names.push(*ident); } Binding::Pattern(pattern) => { names.extend(pattern.idents()); @@ -180,26 +180,26 @@ impl StatementList { names } - /// Returns `true` if the node contains the given token. + /// Returns true if the node contains a identifier reference named 'arguments'. /// /// More information: /// - [ECMAScript specification][spec] /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments #[inline] - pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.statements.iter().any(|stmt| stmt.contains(symbol)) + pub(crate) fn contains_arguments(&self) -> bool { + self.statements.iter().any(Statement::contains_arguments) } - /// Returns true if the node contains a identifier reference named 'arguments'. + /// Returns `true` if the node contains the given token. /// /// More information: /// - [ECMAScript specification][spec] /// - /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments + /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains #[inline] - pub(crate) fn contains_arguments(&self) -> bool { - self.statements.iter().any(Statement::contains_arguments) + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.statements.iter().any(|stmt| stmt.contains(symbol)) } } diff --git a/boa_engine/src/syntax/ast/statement/switch/mod.rs b/boa_engine/src/syntax/ast/statement/switch/mod.rs index cd1edfedce2..92947333bd9 100644 --- a/boa_engine/src/syntax/ast/statement/switch/mod.rs +++ b/boa_engine/src/syntax/ast/statement/switch/mod.rs @@ -33,15 +33,12 @@ impl Case { &self.body } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { - self.condition.contains_arguments() - || self - .body - .statements() - .iter() - .any(Statement::contains_arguments) + self.condition.contains_arguments() || self.body.contains_arguments() } + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { self.condition.contains(symbol) || self @@ -124,6 +121,18 @@ impl Switch { buf } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.val.contains_arguments() + || self.cases.iter().any(Case::contains_arguments) + || matches!(self.default, Some(ref stmts) if stmts.contains_arguments()) + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.val.contains(symbol) || self.cases.iter().any(|case| case.contains(symbol)) + } } impl ToInternedString for Switch { diff --git a/boa_engine/src/syntax/ast/statement/throw.rs b/boa_engine/src/syntax/ast/statement/throw.rs index da1b156ecd5..7020e143498 100644 --- a/boa_engine/src/syntax/ast/statement/throw.rs +++ b/boa_engine/src/syntax/ast/statement/throw.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{statement::Statement, Expression}; +use crate::syntax::ast::{statement::Statement, ContainsSymbol, Expression}; use boa_interner::{Interner, ToInternedString}; /// The `throw` statement throws a user-defined exception. @@ -30,6 +30,16 @@ impl Throw { pub fn new(expression: Expression) -> Self { Self { expression } } + + #[inline] + pub(crate) fn contains_arguments(&self) -> bool { + self.expression.contains_arguments() + } + + #[inline] + pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { + self.expression.contains(symbol) + } } impl ToInternedString for Throw { diff --git a/boa_engine/src/syntax/ast/statement/try/mod.rs b/boa_engine/src/syntax/ast/statement/try/mod.rs index a58475e1397..c1edd1a3fb2 100644 --- a/boa_engine/src/syntax/ast/statement/try/mod.rs +++ b/boa_engine/src/syntax/ast/statement/try/mod.rs @@ -61,20 +61,16 @@ impl Try { self.finally.as_ref().map(Finally::block) } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { - self.block - .statements() - .iter() - .any(Statement::contains_arguments) + self.block.contains_arguments() || matches!(self.catch, Some(ref catch) if catch.contains_arguments()) || matches!(self.finally, Some(ref finally) if finally.contains_arguments()) } + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { - self.block - .statements() - .iter() - .any(|stmt| stmt.contains(symbol)) + self.block.contains(symbol) || matches!(self.catch, Some(ref catch) if catch.contains(symbol)) || matches!(self.finally, Some(ref finally) if finally.contains(symbol)) } @@ -134,6 +130,7 @@ impl Catch { &self.block } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.block .statements() @@ -141,6 +138,7 @@ impl Catch { .any(Statement::contains_arguments) } + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { self.block .statements() @@ -182,6 +180,7 @@ impl Finally { &self.block } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.block .statements() @@ -189,6 +188,7 @@ impl Finally { .any(Statement::contains_arguments) } + #[inline] pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool { self.block .statements() 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 8b3d025a98c..f9cfca9d43c 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs @@ -11,6 +11,7 @@ use super::AssignmentExpression; use crate::syntax::{ ast::{ self, + expression::Identifier, function::{FormalParameter, FormalParameterList, FormalParameterListFlags}, statement::{declaration::Declaration, Return, StatementList}, Expression, Punctuator, @@ -37,7 +38,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct ArrowFunction { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -52,7 +53,7 @@ impl ArrowFunction { allow_await: A, ) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -101,7 +102,7 @@ where ( FormalParameterList::new( Box::new([FormalParameter::new( - Declaration::from_identifier(param.into(), None), + Declaration::from_identifier(param, None), false, )]), flags, diff --git a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs index 662d43ab49f..5fc9316b317 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs @@ -8,14 +8,17 @@ //! [spec]: https://tc39.es/ecma262/#sec-conditional-operator use crate::syntax::{ - ast::{expression::operator::conditional::Conditional, Expression, Punctuator}, + ast::{ + expression::{operator::conditional::Conditional, Identifier}, + Expression, Punctuator, + }, lexer::TokenKind, parser::{ expression::{AssignmentExpression, ShortCircuitExpression}, AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -29,7 +32,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser::expression) struct ConditionalExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -44,7 +47,7 @@ impl ConditionalExpression { allow_await: A, ) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, diff --git a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs index 69073a6b798..8ef936a5c11 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs @@ -10,7 +10,10 @@ use super::ParseError; use crate::syntax::{ ast::{ - expression::operator::{binary::op::ArithmeticOp, Binary}, + expression::{ + operator::{binary::op::ArithmeticOp, Binary}, + Identifier, + }, Expression, Keyword, Punctuator, }, lexer::TokenKind, @@ -19,7 +22,7 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -33,7 +36,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-ExponentiationExpression #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser::expression) struct ExponentiationExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -46,7 +49,7 @@ impl ExponentiationExpression { allow_await: A, ) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { diff --git a/boa_engine/src/syntax/parser/expression/assignment/mod.rs b/boa_engine/src/syntax/parser/expression/assignment/mod.rs index 0065b64ab16..c4dfe9c836f 100644 --- a/boa_engine/src/syntax/parser/expression/assignment/mod.rs +++ b/boa_engine/src/syntax/parser/expression/assignment/mod.rs @@ -15,7 +15,10 @@ mod r#yield; use crate::syntax::{ ast::{ self, - expression::operator::assign::{op::AssignOp, Assign, AssignTarget}, + expression::{ + operator::assign::{op::AssignOp, Assign, AssignTarget}, + Identifier, + }, Expression, Keyword, Punctuator, }, lexer::{Error as LexError, InputElement, TokenKind}, @@ -28,7 +31,7 @@ use crate::syntax::{ AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -54,7 +57,7 @@ pub(super) use exponentiation::ExponentiationExpression; /// [lhs]: ../lhs_expression/struct.LeftHandSideExpression.html #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct AssignmentExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -69,7 +72,7 @@ impl AssignmentExpression { allow_await: A, ) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -210,7 +213,7 @@ where AssignTarget::from_expression(&lhs, cursor.strict_mode(), true) { if let AssignTarget::Identifier(ident) = target { - self.name = Some(ident.sym()); + self.name = Some(ident); } let expr = self.parse(cursor, interner)?; lhs = Assign::new(AssignOp::Assign, target, expr).into(); diff --git a/boa_engine/src/syntax/parser/expression/identifiers.rs b/boa_engine/src/syntax/parser/expression/identifiers.rs index 9f6ac3893a4..4de308f7127 100644 --- a/boa_engine/src/syntax/parser/expression/identifiers.rs +++ b/boa_engine/src/syntax/parser/expression/identifiers.rs @@ -148,7 +148,7 @@ impl TokenParser for BindingIdentifier where R: Read, { - type Output = Sym; + type Output = Identifier; /// Strict mode parsing as per . fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { @@ -176,7 +176,7 @@ where next_token.span().start(), )); } - Ok(*ident) + Ok((*ident).into()) } TokenKind::Keyword((Keyword::Let, _)) if cursor.strict_mode() => { Err(ParseError::lex(LexError::Syntax( @@ -184,7 +184,7 @@ where next_token.span().start(), ))) } - TokenKind::Keyword((Keyword::Let, _)) => Ok(Sym::LET), + TokenKind::Keyword((Keyword::Let, _)) => Ok(Sym::LET.into()), TokenKind::Keyword((Keyword::Yield, _)) if self.allow_yield.0 => { // Early Error: It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield". Err(ParseError::general( @@ -199,10 +199,10 @@ where next_token.span().start(), )) } else { - Ok(Sym::YIELD) + Ok(Sym::YIELD.into()) } } - TokenKind::Keyword((Keyword::Await, _)) if cursor.arrow() => Ok(Sym::AWAIT), + TokenKind::Keyword((Keyword::Await, _)) if cursor.arrow() => Ok(Sym::AWAIT.into()), TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => { // Early Error: It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await". Err(ParseError::general( @@ -210,9 +210,9 @@ where next_token.span().start(), )) } - TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => Ok(Sym::AWAIT), - TokenKind::Keyword((Keyword::Async, _)) => Ok(Sym::ASYNC), - TokenKind::Keyword((Keyword::Of, _)) => Ok(Sym::OF), + TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => Ok(Sym::AWAIT.into()), + TokenKind::Keyword((Keyword::Async, _)) => Ok(Sym::ASYNC.into()), + TokenKind::Keyword((Keyword::Of, _)) => Ok(Sym::OF.into()), _ => Err(ParseError::expected( ["identifier".to_owned()], next_token.to_string(interner), 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 b1807d6cde9..3fb26e60634 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 @@ -13,7 +13,7 @@ use crate::syntax::{ access::{ PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SuperPropertyAccess, }, - Call, New, + Call, Identifier, New, }, Keyword, Punctuator, }, @@ -37,7 +37,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-MemberExpression #[derive(Debug, Clone, Copy)] pub(super) struct MemberExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -46,7 +46,7 @@ impl MemberExpression { /// Creates a new `MemberExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { 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 f0ee09c7d51..c06531ed5a1 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 @@ -16,7 +16,10 @@ mod member; mod template; use crate::syntax::{ - ast::{expression::SuperCall, Expression, Keyword, Punctuator}, + ast::{ + expression::{Identifier, SuperCall}, + Expression, Keyword, Punctuator, + }, lexer::{InputElement, TokenKind}, parser::{ expression::left_hand_side::{ @@ -25,7 +28,7 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -39,7 +42,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-LeftHandSideExpression #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct LeftHandSideExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -48,7 +51,7 @@ impl LeftHandSideExpression { /// Creates a new `LeftHandSideExpression` parser. pub(in crate::syntax::parser) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { diff --git a/boa_engine/src/syntax/parser/expression/mod.rs b/boa_engine/src/syntax/parser/expression/mod.rs index caa1eb691ff..4a32573f130 100644 --- a/boa_engine/src/syntax/parser/expression/mod.rs +++ b/boa_engine/src/syntax/parser/expression/mod.rs @@ -22,9 +22,12 @@ mod tests; use crate::syntax::{ ast::{ self, - expression::operator::{ - binary::op::{BinaryOp, LogicalOp}, - Binary, + expression::{ + operator::{ + binary::op::{BinaryOp, LogicalOp}, + Binary, + }, + Identifier, }, Keyword, Punctuator, }, @@ -34,7 +37,7 @@ use crate::syntax::{ ParseError, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -134,7 +137,7 @@ macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $lo /// [spec]: https://tc39.es/ecma262/#prod-Expression #[derive(Debug, Clone, Copy)] pub(super) struct Expression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -144,7 +147,7 @@ impl Expression { /// Creates a new `Expression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -231,7 +234,7 @@ where /// [spec]: https://tc39.es/ecma262/#prod-ShortCircuitExpression #[derive(Debug, Clone, Copy)] struct ShortCircuitExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -249,7 +252,7 @@ impl ShortCircuitExpression { /// Creates a new `ShortCircuitExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -271,7 +274,7 @@ impl ShortCircuitExpression { previous: PreviousExpr, ) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -383,7 +386,7 @@ where /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression #[derive(Debug, Clone, Copy)] struct BitwiseORExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -393,7 +396,7 @@ impl BitwiseORExpression { /// Creates a new `BitwiseORExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -425,7 +428,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression #[derive(Debug, Clone, Copy)] struct BitwiseXORExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -435,7 +438,7 @@ impl BitwiseXORExpression { /// Creates a new `BitwiseXORExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -467,7 +470,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression #[derive(Debug, Clone, Copy)] struct BitwiseANDExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -477,7 +480,7 @@ impl BitwiseANDExpression { /// Creates a new `BitwiseANDExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -509,7 +512,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#sec-equality-operators #[derive(Debug, Clone, Copy)] struct EqualityExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -519,7 +522,7 @@ impl EqualityExpression { /// Creates a new `EqualityExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -556,7 +559,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#sec-relational-operators #[derive(Debug, Clone, Copy)] struct RelationalExpression { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -566,7 +569,7 @@ impl RelationalExpression { /// Creates a new `RelationalExpression` parser. pub(super) fn new(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -645,7 +648,7 @@ where /// [spec]: https://tc39.es/ecma262/#sec-bitwise-shift-operators #[derive(Debug, Clone, Copy)] struct ShiftExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -654,7 +657,7 @@ impl ShiftExpression { /// Creates a new `ShiftExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { @@ -690,7 +693,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#sec-additive-operators #[derive(Debug, Clone, Copy)] struct AdditiveExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -699,7 +702,7 @@ impl AdditiveExpression { /// Creates a new `AdditiveExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { @@ -731,7 +734,7 @@ expression!( /// [spec]: https://tc39.es/ecma262/#sec-multiplicative-operators #[derive(Debug, Clone, Copy)] struct MultiplicativeExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -740,7 +743,7 @@ impl MultiplicativeExpression { /// Creates a new `MultiplicativeExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { 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 6dcead96a74..2e535436f05 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 @@ -3,7 +3,8 @@ mod tests; use crate::syntax::{ ast::{ - function::function_contains_super, function::AsyncFunction, Keyword, Position, Punctuator, + expression::Identifier, function::function_contains_super, function::AsyncFunction, + Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ @@ -26,7 +27,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression #[derive(Debug, Clone, Copy)] pub(super) struct AsyncFunctionExpression { - name: Option, + name: Option, allow_yield: AllowYield, } @@ -34,7 +35,7 @@ impl AsyncFunctionExpression { /// Creates a new `AsyncFunctionExpression` parser. pub(super) fn new(name: N, allow_yield: Y) -> Self where - N: Into>, + N: Into>, Y: Into, { Self { @@ -71,7 +72,7 @@ where // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". if let Some(name) = name { - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name) { + if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(ParseError::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), match cursor.peek(0, interner)? { 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 e2068b57131..9677da82436 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 @@ -27,7 +27,7 @@ fn check_async_expression() { add.into(), Some( AsyncFunction::new( - Some(add), + Some(add.into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) @@ -58,14 +58,14 @@ fn check_nested_async_expression() { a.into(), Some( AsyncFunction::new( - Some(a), + Some(a.into()), FormalParameterList::default(), vec![DeclarationList::Const( vec![Declaration::from_identifier( b.into(), Some( AsyncFunction::new( - Some(b), + Some(b.into()), FormalParameterList::default(), vec![ Return::new(Some(Literal::from(1).into()), None).into() 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 4b436fc9429..9f61be7eddb 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 @@ -12,7 +12,8 @@ mod tests; use crate::syntax::{ ast::{ - function::function_contains_super, function::AsyncGenerator, Keyword, Position, Punctuator, + expression::Identifier, function::function_contains_super, function::AsyncGenerator, + Keyword, Position, Punctuator, }, lexer::{Error as LexError, TokenKind}, parser::{ @@ -33,14 +34,14 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression #[derive(Debug, Clone, Copy)] pub(super) struct AsyncGeneratorExpression { - name: Option, + name: Option, } impl AsyncGeneratorExpression { /// Creates a new `AsyncGeneratorExpression` parser. pub(in crate::syntax::parser) fn new(name: N) -> Self where - N: Into>, + N: Into>, { Self { name: name.into() } } @@ -80,7 +81,7 @@ where // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict // mode code, it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". if let Some(name) = name { - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name) { + if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(ParseError::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), match cursor.peek(0, interner)? { 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 d68791f26bb..0ae54db4eeb 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 @@ -28,7 +28,7 @@ fn check_async_generator_expr() { add.into(), Some( AsyncGenerator::new( - Some(add), + Some(add.into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) @@ -59,14 +59,14 @@ fn check_nested_async_generator_expr() { a.into(), Some( AsyncGenerator::new( - Some(a), + Some(a.into()), FormalParameterList::default(), vec![DeclarationList::Const( vec![Declaration::from_identifier( b.into(), Some( AsyncGenerator::new( - Some(b), + Some(b.into()), FormalParameterList::default(), vec![ Return::new(Some(Literal::from(1).into()), None).into() 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 befb26de14d..fba2483f95d 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,12 +1,12 @@ use crate::syntax::{ - ast::{function::Class, Keyword}, + ast::{expression::Identifier, function::Class, Keyword}, lexer::TokenKind, parser::{ expression::BindingIdentifier, statement::ClassTail, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -18,7 +18,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-ClassExpression #[derive(Debug, Clone, Copy)] pub(super) struct ClassExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -27,7 +27,7 @@ impl ClassExpression { /// Creates a new `ClassExpression` parser. pub(in crate::syntax::parser) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { 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 c5e104a4da5..03dcef6082e 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 @@ -12,6 +12,7 @@ mod tests; use crate::syntax::{ ast::{ + expression::Identifier, function::{function_contains_super, Function}, Keyword, Position, Punctuator, }, @@ -36,14 +37,14 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression #[derive(Debug, Clone, Copy)] pub(super) struct FunctionExpression { - name: Option, + name: Option, } impl FunctionExpression { /// Creates a new `FunctionExpression` parser. pub(in crate::syntax::parser) fn new(name: N) -> Self where - N: Into>, + N: Into>, { Self { name: name.into() } } @@ -73,8 +74,8 @@ where // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". - if let Some(name) = &name { - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(name) { + if let Some(name) = name { + if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(ParseError::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), match cursor.peek(0, interner)? { 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 c7e5acb0dea..52364a5e857 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 @@ -27,7 +27,7 @@ fn check_function_expression() { add.into(), Some( Function::new( - Some(add), + Some(add.into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) @@ -58,14 +58,14 @@ fn check_nested_function_expression() { a.into(), Some( Function::new( - Some(a), + Some(a.into()), FormalParameterList::default(), vec![DeclarationList::Const( vec![Declaration::from_identifier( b.into(), Some( Function::new( - Some(b), + Some(b.into()), FormalParameterList::default(), vec![ Return::new(Some(Literal::from(1).into()), None).into() @@ -99,7 +99,7 @@ fn check_function_non_reserved_keyword() { $interner.get_or_intern_static("add", utf16!("add")).into(), Some( Function::new( - Some($interner.get_or_intern_static($keyword, utf16!($keyword))), + Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(1).into()), None).into()].into(), ) 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 e531a7d5900..24ae0cb4c24 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 @@ -12,6 +12,7 @@ mod tests; use crate::syntax::{ ast::{ + expression::Identifier, function::{function_contains_super, Generator}, Position, Punctuator, }, @@ -36,14 +37,14 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression #[derive(Debug, Clone, Copy)] pub(super) struct GeneratorExpression { - name: Option, + name: Option, } impl GeneratorExpression { /// Creates a new `GeneratorExpression` parser. pub(in crate::syntax::parser) fn new(name: N) -> Self where - N: Into>, + N: Into>, { Self { name: name.into() } } @@ -75,8 +76,8 @@ where // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". - if let Some(name) = &name { - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(name) { + if let Some(name) = name { + if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(ParseError::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), match cursor.peek(0, interner)? { 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 d54010a88b0..777e5b9bcd3 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 @@ -24,7 +24,7 @@ fn check_generator_function_expression() { gen.into(), Some( Generator::new( - Some(gen), + Some(gen.into()), FormalParameterList::default(), vec![ Expression::from(Yield::new(Some(Literal::from(1).into()), false)) @@ -56,7 +56,7 @@ fn check_generator_function_delegate_yield_expression() { gen.into(), Some( Generator::new( - Some(gen), + Some(gen.into()), FormalParameterList::default(), vec![ Expression::from(Yield::new(Some(Literal::from(1).into()), true)) diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs index 2f944153f79..ecc94442c70 100644 --- a/boa_engine/src/syntax/parser/expression/primary/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs @@ -71,7 +71,7 @@ pub(in crate::syntax::parser) use object_initializer::Initializer; /// [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression #[derive(Debug, Clone, Copy)] pub(super) struct PrimaryExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -80,7 +80,7 @@ impl PrimaryExpression { /// Creates a new `PrimaryExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { @@ -288,7 +288,7 @@ where /// [spec]: https://tc39.es/ecma262/#prod-CoverParenthesizedExpressionAndArrowParameterList #[derive(Debug, Clone, Copy)] pub(super) struct CoverParenthesizedExpressionAndArrowParameterList { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -297,7 +297,7 @@ impl CoverParenthesizedExpressionAndArrowParameterList { /// Creates a new `CoverParenthesizedExpressionAndArrowParameterList` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { @@ -321,7 +321,7 @@ where Expression(ast::Expression), SpreadObject(Vec), SpreadArray(Vec), - SpreadBinding(Sym), + SpreadBinding(Identifier), } let _timer = Profiler::global().start_event( @@ -455,7 +455,7 @@ where parameters.push(parameter); } InnerExpression::SpreadBinding(ident) => { - let declaration = Declaration::from_identifier(ident.into(), None); + let declaration = Declaration::from_identifier(ident, None); let parameter = FormalParameter::new(declaration, true); parameters.push(parameter); } @@ -492,13 +492,13 @@ fn expression_to_formal_parameters( span: Span, ) -> Result<(), ParseError> { match node { - ast::Expression::Identifier(identifier) if strict && identifier.sym() == Sym::EVAL => { + ast::Expression::Identifier(identifier) if strict && *identifier == Sym::EVAL => { return Err(ParseError::general( "parameter name 'eval' not allowed in strict mode", span.start(), )); } - ast::Expression::Identifier(identifier) if strict && identifier.sym() == Sym::ARGUMENTS => { + ast::Expression::Identifier(identifier) if strict && *identifier == Sym::ARGUMENTS => { return Err(ParseError::general( "parameter name 'arguments' not allowed in strict mode", span.start(), 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 31e0dd328ff..26d2f7414ba 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 @@ -12,7 +12,10 @@ mod tests; use crate::syntax::{ ast::{ - expression::literal::{self, Literal}, + expression::{ + literal::{self, Literal}, + Identifier, + }, function::{function_contains_super, has_direct_super}, function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, property::{self, MethodDefinition}, @@ -205,8 +208,8 @@ where }; return Ok(property::PropertyDefinition::MethodDefinition( - method, property_name, + method, )); } let (class_element_name, method) = @@ -229,8 +232,8 @@ where } return Ok(property::PropertyDefinition::MethodDefinition( - method, property_name, + method, )); } _ => {} @@ -258,8 +261,8 @@ where match class_element_name { property::ClassElementName::PropertyName(property_name) => { return Ok(property::PropertyDefinition::MethodDefinition( - method, property_name, + method, )) } property::ClassElementName::PrivateIdentifier(_) => { @@ -328,12 +331,12 @@ where } Ok(property::PropertyDefinition::MethodDefinition( + property_name, MethodDefinition::Get(Function::new( None, FormalParameterList::default(), body, )), - property_name, )) } // MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] } @@ -389,8 +392,8 @@ where } Ok(property::PropertyDefinition::MethodDefinition( - MethodDefinition::Set(Function::new(None, parameters, body)), property_name, + MethodDefinition::Set(Function::new(None, parameters, body)), )) } // MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] } @@ -468,8 +471,8 @@ where } Ok(property::PropertyDefinition::MethodDefinition( - MethodDefinition::Ordinary(Function::new(None, params, body)), property_name, + MethodDefinition::Ordinary(Function::new(None, params, body)), )) } } @@ -603,7 +606,7 @@ where /// [spec]: https://tc39.es/ecma262/#prod-Initializer #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct Initializer { - name: Option, + name: Option, allow_in: AllowIn, allow_yield: AllowYield, allow_await: AllowAwait, @@ -618,7 +621,7 @@ impl Initializer { allow_await: A, ) -> Self where - N: Into>, + N: Into>, I: Into, Y: Into, A: Into, @@ -972,12 +975,11 @@ where cursor.expect(Punctuator::Assign, "CoverInitializedName", interner)?; - let expr = AssignmentExpression::new(ident.sym(), true, self.allow_yield, self.allow_await) + let expr = AssignmentExpression::new(ident, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; Ok(property::PropertyDefinition::CoverInitializedName( - ident.sym(), - expr, + ident, expr, )) } } 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 ebc7eebb4c4..b029c38ec16 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 @@ -64,12 +64,12 @@ fn check_object_short_function() { Literal::from(true).into(), ), PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -102,6 +102,7 @@ fn check_object_short_function_arguments() { Literal::from(true).into(), ), PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Ordinary(Function::new( None, FormalParameterList { @@ -117,7 +118,6 @@ fn check_object_short_function_arguments() { }, StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -149,12 +149,12 @@ fn check_object_getter() { Literal::from(true).into(), ), PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Get(Function::new( None, FormalParameterList::default(), StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -186,6 +186,7 @@ fn check_object_setter() { Literal::from(true).into(), ), PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Set(Function::new( None, FormalParameterList { @@ -201,7 +202,6 @@ fn check_object_setter() { }, StatementList::default(), )), - interner.get_or_intern_static("b", utf16!("b")).into(), ), ]; @@ -228,12 +228,12 @@ fn check_object_short_function_get() { let mut interner = Interner::default(); let object_properties = vec![PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("get", utf16!("get")).into(), MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), StatementList::default(), )), - interner.get_or_intern_static("get", utf16!("get")).into(), )]; check_parser( @@ -258,12 +258,12 @@ fn check_object_short_function_set() { let mut interner = Interner::default(); let object_properties = vec![PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("set", utf16!("set")).into(), MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), StatementList::default(), )), - interner.get_or_intern_static("set", utf16!("set")).into(), )]; check_parser( @@ -402,12 +402,12 @@ fn check_async_method() { let mut interner = Interner::default(); let object_properties = vec![PropertyDefinition::MethodDefinition( + interner.get_or_intern_static("dive", utf16!("dive")).into(), MethodDefinition::Async(AsyncFunction::new( None, FormalParameterList::default(), StatementList::default(), )), - interner.get_or_intern_static("dive", utf16!("dive")).into(), )]; check_parser( @@ -432,14 +432,14 @@ fn check_async_generator_method() { let mut interner = Interner::default(); let object_properties = vec![PropertyDefinition::MethodDefinition( + interner + .get_or_intern_static("vroom", utf16!("vroom")) + .into(), MethodDefinition::AsyncGenerator(AsyncGenerator::new( None, FormalParameterList::default(), StatementList::default(), )), - interner - .get_or_intern_static("vroom", utf16!("vroom")) - .into(), )]; check_parser( @@ -486,12 +486,12 @@ fn check_async_ordinary_method() { let mut interner = Interner::default(); let object_properties = vec![PropertyDefinition::MethodDefinition( + PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), MethodDefinition::Ordinary(Function::new( None, FormalParameterList::default(), StatementList::default(), )), - PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), )]; check_parser( diff --git a/boa_engine/src/syntax/parser/expression/unary.rs b/boa_engine/src/syntax/parser/expression/unary.rs index 958f40f9909..9c4590b8d2c 100644 --- a/boa_engine/src/syntax/parser/expression/unary.rs +++ b/boa_engine/src/syntax/parser/expression/unary.rs @@ -9,7 +9,10 @@ use crate::syntax::{ ast::{ - expression::operator::{unary::op::UnaryOp, Unary}, + expression::{ + operator::{unary::op::UnaryOp, Unary}, + Identifier, + }, Expression, Keyword, Punctuator, }, lexer::{Error as LexError, TokenKind}, @@ -18,7 +21,7 @@ use crate::syntax::{ AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -32,7 +35,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct UnaryExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -41,7 +44,7 @@ impl UnaryExpression { /// Creates a new `UnaryExpression` parser. pub(in crate::syntax::parser) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { diff --git a/boa_engine/src/syntax/parser/expression/update.rs b/boa_engine/src/syntax/parser/expression/update.rs index 2e433ccd697..ee36510a9dd 100644 --- a/boa_engine/src/syntax/parser/expression/update.rs +++ b/boa_engine/src/syntax/parser/expression/update.rs @@ -8,7 +8,10 @@ use super::left_hand_side::LeftHandSideExpression; use crate::syntax::{ ast::{ - expression::operator::{unary::op::UnaryOp, Unary}, + expression::{ + operator::{unary::op::UnaryOp, Unary}, + Identifier, + }, Expression, Punctuator, }, lexer::{Error as LexError, TokenKind}, @@ -29,7 +32,7 @@ use std::io::Read; /// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression #[derive(Debug, Clone, Copy)] pub(super) struct UpdateExpression { - name: Option, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } @@ -38,7 +41,7 @@ impl UpdateExpression { /// Creates a new `UpdateExpression` parser. pub(super) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where - N: Into>, + N: Into>, Y: Into, A: Into, { diff --git a/boa_engine/src/syntax/parser/function/mod.rs b/boa_engine/src/syntax/parser/function/mod.rs index 3b420589b89..03282f94bad 100644 --- a/boa_engine/src/syntax/parser/function/mod.rs +++ b/boa_engine/src/syntax/parser/function/mod.rs @@ -118,7 +118,7 @@ where if next_param.init().is_some() { flags |= FormalParameterListFlags::HAS_EXPRESSIONS; } - if next_param.names().contains(&Sym::ARGUMENTS) { + if next_param.names().contains(&Sym::ARGUMENTS.into()) { flags |= FormalParameterListFlags::HAS_ARGUMENTS; } @@ -335,7 +335,7 @@ where }) .transpose()?; - Declaration::from_identifier(params.into(), init) + Declaration::from_identifier(params, init) } }; Ok(Self::Output::new(declaration, true)) @@ -442,7 +442,7 @@ where None }; - Declaration::from_identifier(ident.into(), init) + Declaration::from_identifier(ident, init) } }; Ok(Self::Output::new(declaration, false)) diff --git a/boa_engine/src/syntax/parser/function/tests.rs b/boa_engine/src/syntax/parser/function/tests.rs index 2fe643e31aa..e05c9bebc50 100644 --- a/boa_engine/src/syntax/parser/function/tests.rs +++ b/boa_engine/src/syntax/parser/function/tests.rs @@ -30,7 +30,7 @@ fn check_basic() { check_parser( "function foo(a) { return a; }", vec![Function::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -61,7 +61,7 @@ fn check_duplicates_strict_off() { check_parser( "function foo(a, a) { return a; }", vec![Function::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( @@ -112,7 +112,7 @@ fn check_basic_semicolon_insertion() { check_parser( "function foo(a) { return a }", vec![Function::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -143,7 +143,7 @@ fn check_empty_return() { check_parser( "function foo(a) { return; }", vec![Function::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -169,7 +169,7 @@ fn check_empty_return_semicolon_insertion() { check_parser( "function foo(a) { return }", vec![Function::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -195,7 +195,7 @@ fn check_rest_operator() { check_parser( "function foo(a, ...b) {}", vec![Function::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( @@ -468,7 +468,7 @@ fn check_arrow_assignment() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -510,7 +510,7 @@ fn check_arrow_assignment_nobrackets() { interner.get_or_intern_static("foo", utf16!("foo")).into(), Some( ArrowFunction::new( - interner.get_or_intern_static("foo", utf16!("foo")).into(), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -552,7 +552,7 @@ fn check_arrow_assignment_noparenthesis() { interner.get_or_intern_static("foo", utf16!("foo")).into(), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -594,7 +594,7 @@ fn check_arrow_assignment_noparenthesis_nobrackets() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([FormalParameter::new( Declaration::from_identifier( @@ -636,7 +636,7 @@ fn check_arrow_assignment_2arg() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( @@ -687,7 +687,7 @@ fn check_arrow_assignment_2arg_nobrackets() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( @@ -738,7 +738,7 @@ fn check_arrow_assignment_3arg() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( @@ -796,7 +796,7 @@ fn check_arrow_assignment_3arg_nobrackets() { Identifier::new(interner.get_or_intern_static("foo", utf16!("foo"))), Some( ArrowFunction::new( - Some(interner.get_or_intern_static("foo", utf16!("foo"))), + Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), FormalParameterList { parameters: Box::new([ FormalParameter::new( diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs index adce0c94df5..2b0ca4df6dc 100644 --- a/boa_engine/src/syntax/parser/mod.rs +++ b/boa_engine/src/syntax/parser/mod.rs @@ -22,7 +22,7 @@ use crate::{ }, Context, JsString, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -30,7 +30,8 @@ pub use self::error::{ParseError, ParseResult}; pub(in crate::syntax) use expression::RESERVED_IDENTIFIERS_STRICT; use super::ast::{ - function::FormalParameterList, statement::StatementList, ContainsSymbol, Position, + expression::Identifier, function::FormalParameterList, statement::StatementList, + ContainsSymbol, Position, }; /// Trait implemented by parsers. @@ -307,7 +308,8 @@ impl Script { let mut var_declared_names = FxHashSet::default(); statement_list.var_declared_names(&mut var_declared_names); let lexically_declared_names = statement_list.lexically_declared_names(); - let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); + let mut lexically_declared_names_map: FxHashMap = + FxHashMap::default(); for (name, is_function_declaration) in &lexically_declared_names { if let Some(existing_is_function_declaration) = lexically_declared_names_map.get(name) @@ -334,7 +336,7 @@ impl Script { )); } if !is_function_declaration { - let name_str = context.interner().resolve_expect(*name); + let name_str = context.interner().resolve_expect(name.sym()); let desc = context .realm .global_property_map diff --git a/boa_engine/src/syntax/parser/statement/block/mod.rs b/boa_engine/src/syntax/parser/statement/block/mod.rs index 5da1d6350c9..da231cfa31e 100644 --- a/boa_engine/src/syntax/parser/statement/block/mod.rs +++ b/boa_engine/src/syntax/parser/statement/block/mod.rs @@ -12,11 +12,11 @@ mod tests; use super::StatementList; use crate::syntax::{ - ast::{statement, Punctuator}, + ast::{expression::Identifier, statement, Punctuator}, lexer::TokenKind, parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -94,7 +94,7 @@ where cursor.expect(Punctuator::CloseBlock, "block", interner)?; let lexically_declared_names = statement_list.lexically_declared_names(); - let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); + let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); for (name, is_function_declaration) in &lexically_declared_names { if let Some(existing_is_function_declaration) = lexically_declared_names_map.get(name) { if !(!cursor.strict_mode() diff --git a/boa_engine/src/syntax/parser/statement/block/tests.rs b/boa_engine/src/syntax/parser/statement/block/tests.rs index bf06a72bcc9..3c11fe256ea 100644 --- a/boa_engine/src/syntax/parser/statement/block/tests.rs +++ b/boa_engine/src/syntax/parser/statement/block/tests.rs @@ -74,7 +74,7 @@ fn non_empty() { }", vec![ Function::new( - hello.into(), + Some(hello.into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(10).into()), None).into()].into(), ) @@ -111,7 +111,7 @@ fn hoisting() { }", vec![ Function::new( - Some(hello), + Some(hello.into()), FormalParameterList::default(), vec![Return::new(Some(Literal::from(10).into()), None).into()].into(), ) 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 efc273759fb..6c766ef0ed1 100644 --- a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs @@ -71,8 +71,9 @@ where None } else { - let label = - LabelIdentifier::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; + let label = LabelIdentifier::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)? + .sym(); cursor.expect_semicolon("break statement", interner)?; Some(label) 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 65d33b44f52..0d3fabdd199 100644 --- a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs @@ -71,8 +71,9 @@ where None } else { - let label = - LabelIdentifier::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; + let label = LabelIdentifier::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)? + .sym(); cursor.expect_semicolon("continue statement", interner)?; Some(label) 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 b9577b883ac..4867352f588 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 @@ -15,7 +15,11 @@ fn async_function_declaration() { check_parser( "async function hello() {}", vec![AsyncFunction::new( - Some(interner.get_or_intern_static("hello", utf16!("hello"))), + Some( + interner + .get_or_intern_static("hello", utf16!("hello")) + .into(), + ), FormalParameterList::default(), StatementList::default(), ) @@ -31,7 +35,11 @@ fn async_function_declaration_keywords() { check_parser( "async function yield() {}", vec![AsyncFunction::new( - Some(interner.get_or_intern_static("yield", utf16!("yield"))), + Some( + interner + .get_or_intern_static("yield", utf16!("yield")) + .into(), + ), FormalParameterList::default(), StatementList::default(), ) @@ -43,7 +51,11 @@ fn async_function_declaration_keywords() { check_parser( "async function await() {}", vec![AsyncFunction::new( - Some(interner.get_or_intern_static("await", utf16!("await"))), + Some( + interner + .get_or_intern_static("await", utf16!("await")) + .into(), + ), FormalParameterList::default(), StatementList::default(), ) 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 3fc188f7f59..e2e46d76c35 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 @@ -14,7 +14,7 @@ fn async_generator_function_declaration() { check_parser( "async function* gen() {}", vec![AsyncGenerator::new( - Some(interner.get_or_intern_static("gen", utf16!("gen"))), + Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), StatementList::default(), ) 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 c12ce4e3a22..dac3d43057a 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 @@ -4,6 +4,7 @@ mod tests; use crate::syntax::{ ast::{ self, + expression::Identifier, function::{ self, function_contains_super, has_direct_super, Class, FormalParameterList, Function, }, @@ -73,7 +74,7 @@ where BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)? } - _ if self.is_default.0 => Sym::DEFAULT, + _ if self.is_default.0 => Sym::DEFAULT.into(), _ => { return Err(ParseError::unexpected( token.to_string(interner), @@ -98,14 +99,18 @@ where /// [spec]: https://tc39.es/ecma262/#prod-ClassTail #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct ClassTail { - name: Sym, + name: Identifier, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassTail { /// Creates a new `ClassTail` parser. - pub(in crate::syntax::parser) fn new(name: Sym, allow_yield: Y, allow_await: A) -> Self + pub(in crate::syntax::parser) fn new( + name: Identifier, + allow_yield: Y, + allow_await: A, + ) -> Self where Y: Into, A: Into, @@ -243,14 +248,18 @@ where /// [spec]: https://tc39.es/ecma262/#prod-ClassBody #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct ClassBody { - name: Sym, + name: Identifier, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassBody { /// Creates a new `ClassBody` parser. - pub(in crate::syntax::parser) fn new(name: Sym, allow_yield: Y, allow_await: A) -> Self + pub(in crate::syntax::parser) fn new( + name: Identifier, + allow_yield: Y, + allow_await: A, + ) -> Self where Y: Into, A: Into, @@ -510,14 +519,18 @@ pub(in crate::syntax) enum PrivateElement { /// [spec]: https://tc39.es/ecma262/#prod-ClassElement #[derive(Debug, Clone, Copy)] pub(in crate::syntax::parser) struct ClassElement { - name: Sym, + name: Identifier, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassElement { /// Creates a new `ClassElement` parser. - pub(in crate::syntax::parser) fn new(name: Sym, allow_yield: Y, allow_await: A) -> Self + pub(in crate::syntax::parser) fn new( + name: Identifier, + allow_yield: Y, + allow_await: A, + ) -> Self where Y: Into, A: Into, @@ -626,7 +639,7 @@ where .parse(cursor, interner)?; let lexically_declared_names = statement_list.lexically_declared_names(); - let mut lexically_declared_names_map: FxHashMap = + let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); for (name, is_function_declaration) in &lexically_declared_names { if let Some(existing_is_function_declaration) = @@ -1073,7 +1086,7 @@ where let strict = cursor.strict_mode(); cursor.set_strict_mode(true); let rhs = AssignmentExpression::new( - name, + Some(name.into()), true, self.allow_yield, self.allow_await, @@ -1162,7 +1175,7 @@ where let strict = cursor.strict_mode(); cursor.set_strict_mode(true); let rhs = AssignmentExpression::new( - name.literal(), + name.literal().map(Into::into), true, self.allow_yield, self.allow_await, 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 6d5d918bc35..d424050f8ea 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 @@ -29,7 +29,7 @@ fn check_async_ordinary_method() { } ", [Class::new( - Some(interner.get_or_intern_static("A", utf16!("A"))), + Some(interner.get_or_intern_static("A", utf16!("A")).into()), None, None, elements.into(), @@ -55,7 +55,7 @@ fn check_async_field_initialization() { } ", [Class::new( - Some(interner.get_or_intern_static("A", utf16!("A"))), + Some(interner.get_or_intern_static("A", utf16!("A")).into()), None, None, elements.into(), @@ -80,7 +80,7 @@ fn check_async_field() { } ", [Class::new( - interner.get_or_intern_static("A", utf16!("A")).into(), + Some(interner.get_or_intern_static("A", utf16!("A")).into()), None, None, elements.into(), 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 fdf8aedef09..84f49832034 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 @@ -15,7 +15,11 @@ fn function_declaration() { check_parser( "function hello() {}", vec![Function::new( - Some(interner.get_or_intern_static("hello", utf16!("hello"))), + Some( + interner + .get_or_intern_static("hello", utf16!("hello")) + .into(), + ), FormalParameterList::default(), StatementList::default(), ) @@ -30,7 +34,11 @@ fn function_declaration_keywords() { macro_rules! genast { ($keyword:literal, $interner:expr) => { vec![Function::new( - Some($interner.get_or_intern_static($keyword, utf16!($keyword))), + Some( + $interner + .get_or_intern_static($keyword, utf16!($keyword)) + .into(), + ), FormalParameterList::default(), StatementList::default(), ) 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 66086920ab3..4499958fffb 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 @@ -14,7 +14,7 @@ fn generator_function_declaration() { check_parser( "function* gen() {}", vec![Generator::new( - Some(interner.get_or_intern_static("gen", utf16!("gen"))), + Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), StatementList::default(), ) 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 0f042882892..6305728856c 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -22,6 +22,7 @@ use self::{ use crate::syntax::{ ast::statement::StatementList, ast::{ + expression::Identifier, function::{function_contains_super, FormalParameterList}, Keyword, Position, Punctuator, Statement, }, @@ -140,7 +141,7 @@ fn parse_callable_declaration( c: &C, cursor: &mut Cursor, interner: &mut Interner, -) -> Result<(Sym, FormalParameterList, StatementList), ParseError> { +) -> Result<(Identifier, FormalParameterList, StatementList), ParseError> { let next_token = cursor.peek(0, interner)?; let name = if let Some(token) = next_token { match token.kind() { @@ -152,7 +153,7 @@ fn parse_callable_declaration( c.error_context(), )); } - Sym::DEFAULT + Sym::DEFAULT.into() } _ => BindingIdentifier::new(c.name_allow_yield(), c.name_allow_await()) .parse(cursor, interner)?, @@ -163,7 +164,7 @@ fn parse_callable_declaration( // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code, // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name) { + if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { return Err(ParseError::lex(LexError::Syntax( "Unexpected eval or arguments in strict mode".into(), match cursor.peek(0, interner)? { diff --git a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs index a5c65705b6d..b6b5122b8ed 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs @@ -281,7 +281,7 @@ where let declaration = Pattern::Object(bindings.into()); - if declaration.idents().contains(&Sym::LET) { + if declaration.idents().contains(&Sym::LET.into()) { return Err(ParseError::lex(LexError::Syntax( "'let' is disallowed as a lexically bound name".into(), position, @@ -314,7 +314,7 @@ where let declaration = Pattern::Array(bindings.into()); - if declaration.idents().contains(&Sym::LET) { + if declaration.idents().contains(&Sym::LET.into()) { return Err(ParseError::lex(LexError::Syntax( "'let' is disallowed as a lexically bound name".into(), position, @@ -351,7 +351,7 @@ where } else { None }; - Ok(Declaration::from_identifier(ident.into(), init)) + Ok(Declaration::from_identifier(ident, init)) } } } 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 89b7f25c389..0d29cf3aedd 100644 --- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs +++ b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs @@ -52,8 +52,9 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("Label", "Parsing"); - let name = - LabelIdentifier::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; + let name = LabelIdentifier::new(self.allow_yield, self.allow_await) + .parse(cursor, interner)? + .sym(); cursor.expect(Punctuator::Colon, "Labelled Statement", interner)?; diff --git a/boa_engine/src/syntax/parser/statement/mod.rs b/boa_engine/src/syntax/parser/statement/mod.rs index 5753ef77318..ee4c0ffcce8 100644 --- a/boa_engine/src/syntax/parser/statement/mod.rs +++ b/boa_engine/src/syntax/parser/statement/mod.rs @@ -450,7 +450,7 @@ where let property_name = PropertyName::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; if let Some(name) = property_name.prop_name() { - property_names.push(name); + property_names.push(name.into()); } cursor.expect( TokenKind::Punctuator(Punctuator::Colon), @@ -574,14 +574,14 @@ where .parse(cursor, interner)?; patterns.push(PatternObjectElement::SingleName { ident: name, - name: name.into(), + name: name.sym().into(), default_init: Some(init), }); } _ => { patterns.push(PatternObjectElement::SingleName { ident: name, - name: name.into(), + name: name.sym().into(), default_init: None, }); } diff --git a/boa_engine/src/syntax/parser/statement/switch/mod.rs b/boa_engine/src/syntax/parser/statement/switch/mod.rs index 6ed53420374..c09f26d4543 100644 --- a/boa_engine/src/syntax/parser/statement/switch/mod.rs +++ b/boa_engine/src/syntax/parser/statement/switch/mod.rs @@ -2,14 +2,14 @@ mod tests; use crate::syntax::{ - ast::{statement, statement::Switch, Keyword, Punctuator}, + ast::{expression::Identifier, statement, statement::Switch, Keyword, Punctuator}, lexer::TokenKind, parser::{ expression::Expression, statement::StatementList, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -197,7 +197,7 @@ where default_clause.var_declared_names(&mut var_declared_names); } - let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); + let mut lexically_declared_names_map: FxHashMap = FxHashMap::default(); for (name, is_function_declaration) in &lexically_declared_names { if let Some(existing_is_function_declaration) = lexically_declared_names_map.get(name) { if !(!cursor.strict_mode() 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 6bed1a3e300..76e0d4676c8 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs @@ -98,13 +98,13 @@ where let lexically_declared_names = catch_block.lexically_declared_names(); match &catch_param { Some(Binding::Identifier(ident)) => { - if lexically_declared_names.contains(&(ident.sym(), false)) { + if lexically_declared_names.contains(&(*ident, false)) { return Err(ParseError::general( "catch parameter identifier declared in catch body", position, )); } - if lexically_declared_names.contains(&(ident.sym(), true)) { + if lexically_declared_names.contains(&(*ident, true)) { return Err(ParseError::general( "catch parameter identifier declared in catch body", position, @@ -197,7 +197,7 @@ where TokenKind::Identifier(_) => { let ident = BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - Ok(Binding::Identifier(ident.into())) + Ok(Binding::Identifier(ident)) } _ => Err(ParseError::unexpected( token.to_string(interner), 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 aff8c6d7d1e..6f369715441 100644 --- a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs +++ b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs @@ -183,12 +183,12 @@ fn check_inline_with_binding_pattern_object() { Some( Pattern::from(vec![ PatternObjectElement::SingleName { - ident: a, + ident: a.into(), name: PropertyName::Literal(a), default_init: None, }, PatternObjectElement::SingleName { - ident: interner.get_or_intern_static("c", utf16!("c")), + ident: interner.get_or_intern_static("c", utf16!("c")).into(), name: PropertyName::Literal( interner.get_or_intern_static("b", utf16!("b")), ), @@ -217,11 +217,11 @@ fn check_inline_with_binding_pattern_array() { Some( Pattern::from(vec![ PatternArrayElement::SingleName { - ident: interner.get_or_intern_static("a", utf16!("a")), + ident: interner.get_or_intern_static("a", utf16!("a")).into(), default_init: None, }, PatternArrayElement::SingleName { - ident: interner.get_or_intern_static("b", utf16!("b")), + ident: interner.get_or_intern_static("b", utf16!("b")).into(), default_init: None, }, ]) diff --git a/boa_engine/src/syntax/parser/statement/variable/mod.rs b/boa_engine/src/syntax/parser/statement/variable/mod.rs index 8f9d14846ae..5669564985d 100644 --- a/boa_engine/src/syntax/parser/statement/variable/mod.rs +++ b/boa_engine/src/syntax/parser/statement/variable/mod.rs @@ -230,7 +230,7 @@ where } else { None }; - Ok(Declaration::from_identifier(ident.into(), init)) + Ok(Declaration::from_identifier(ident, init)) } } } diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests.rs index 7daad8c3321..30154381ca1 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/boa_engine/src/syntax/parser/tests.rs @@ -99,7 +99,9 @@ fn assign_operator_precedence() { #[test] fn hoisting() { let mut interner = Interner::default(); - let hello = interner.get_or_intern_static("hello", utf16!("hello")); + let hello = interner + .get_or_intern_static("hello", utf16!("hello")) + .into(); let a = interner.get_or_intern_static("a", utf16!("a")); check_parser( r" @@ -117,7 +119,7 @@ fn hoisting() { DeclarationList::Var( vec![Declaration::from_identifier( a.into(), - Some(Call::new(Identifier::new(hello).into(), Box::default()).into()), + Some(Call::new(hello.into(), Box::default()).into()), )] .into(), ) diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 1e9a49b182e..2fd5dc0566e 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -18,7 +18,7 @@ use crate::{ internal_methods::get_prototype_from_constructor, JsObject, ObjectData, PrivateElement, }, property::PropertyDescriptor, - syntax::ast::function::FormalParameterList, + syntax::ast::{expression::Identifier, function::FormalParameterList}, vm::call_frame::GeneratorResumeKind, vm::{call_frame::FinallyReturn, CallFrame, Opcode}, Context, JsResult, JsString, JsValue, @@ -82,7 +82,7 @@ pub struct CodeBlock { /// Property field names. #[unsafe_ignore_trace] - pub(crate) names: Vec, + pub(crate) names: Vec, /// Locators for all bindings in the codeblock. #[unsafe_ignore_trace] @@ -248,7 +248,7 @@ impl CodeBlock { format!( "{:04}: '{}'", operand, - interner.resolve_expect(self.bindings[operand as usize].name()), + interner.resolve_expect(self.bindings[operand as usize].name().sym()), ) } Opcode::GetPropertyByName @@ -274,7 +274,7 @@ impl CodeBlock { *pc += size_of::(); format!( "{operand:04}: '{}'", - interner.resolve_expect(self.names[operand as usize]), + interner.resolve_expect(self.names[operand as usize].sym()), ) } Opcode::Pop @@ -431,7 +431,7 @@ impl ToInternedString for CodeBlock { for (i, binding_locator) in self.bindings.iter().enumerate() { f.push_str(&format!( " {i:04}: {}\n", - interner.resolve_expect(binding_locator.name()) + interner.resolve_expect(binding_locator.name().sym()) )); } } @@ -1350,8 +1350,8 @@ impl JsObject { for param in code.params.parameters.iter() { has_parameter_expressions = has_parameter_expressions || param.init().is_some(); - arguments_in_parameter_names = - arguments_in_parameter_names || param.names().contains(&Sym::ARGUMENTS); + arguments_in_parameter_names = arguments_in_parameter_names + || param.names().contains(&Sym::ARGUMENTS.into()); is_simple_parameter_list = is_simple_parameter_list && !param.is_rest_param() && param.is_identifier() diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 774f86442b7..17d27b6a50a 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -480,7 +480,7 @@ impl Context { if binding_locator.is_global() { let key = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .into_common(false); self.global_bindings_mut().entry(key).or_insert( PropertyDescriptor::builder() @@ -507,7 +507,7 @@ impl Context { if binding_locator.is_global() { let key = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .into_common::(false) .into(); crate::object::internal_methods::global::global_set_no_receiver( @@ -555,7 +555,7 @@ impl Context { } else { let key: JsString = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .into_common(false); match self.global_bindings_mut().get(&key) { Some(desc) => match desc.kind() { @@ -592,7 +592,7 @@ impl Context { } else { let name = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .to_string(); return self.throw_reference_error(format!("{name} is not initialized",)); }; @@ -613,7 +613,7 @@ impl Context { } else { let key: JsString = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .into_common(false); match self.global_bindings_mut().get(&key) { Some(desc) => match desc.kind() { @@ -657,7 +657,7 @@ impl Context { { let key: JsString = self .interner() - .resolve_expect(binding_locator.name()) + .resolve_expect(binding_locator.name().sym()) .into_common(false); let exists = self.global_bindings_mut().contains_key(&key); @@ -690,7 +690,7 @@ impl Context { ) { self.throw_reference_error(format!( "cannot access '{}' before initialization", - self.interner().resolve_expect(binding_locator.name()) + self.interner().resolve_expect(binding_locator.name().sym()) ))?; } } @@ -753,7 +753,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name: PropertyKey = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); let result = object.get(name, self)?; @@ -803,7 +803,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name: PropertyKey = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); @@ -821,7 +821,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false); object.__define_own_property__( name.into(), @@ -851,7 +851,7 @@ impl Context { .expect("method must be function object") .set_home_object(object.clone()); let name = self.vm.frame().code.names[index as usize]; - let name = self.interner().resolve_expect(name); + let name = self.interner().resolve_expect(name.sym()); object.__define_own_property__( name.into_common::(false).into(), PropertyDescriptor::builder() @@ -933,7 +933,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); let set = object @@ -967,7 +967,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); let set = object @@ -1045,7 +1045,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); let get = object @@ -1079,7 +1079,7 @@ impl Context { let name = self.vm.frame().code.names[index as usize]; let name = self .interner() - .resolve_expect(name) + .resolve_expect(name.sym()) .into_common::(false) .into(); let get = object @@ -1156,10 +1156,10 @@ impl Context { let object = self.vm.pop(); if let Some(object) = object.as_object() { let mut object_borrow_mut = object.borrow_mut(); - match object_borrow_mut.get_private_element(name) { + match object_borrow_mut.get_private_element(name.sym()) { Some(PrivateElement::Field(_)) => { object_borrow_mut - .set_private_element(name, PrivateElement::Field(value)); + .set_private_element(name.sym(), PrivateElement::Field(value)); } Some(PrivateElement::Method(_)) => { return self.throw_type_error("private method is not writable"); @@ -1193,13 +1193,14 @@ impl Context { if let Some(PrivateElement::Accessor { getter: _, setter: Some(setter), - }) = object_borrow_mut.get_private_element(name) + }) = object_borrow_mut.get_private_element(name.sym()) { let setter = setter.clone(); drop(object_borrow_mut); setter.call(&object.clone().into(), &[value], self)?; } else { - object_borrow_mut.set_private_element(name, PrivateElement::Field(value)); + object_borrow_mut + .set_private_element(name.sym(), PrivateElement::Field(value)); } } else { return self.throw_type_error("cannot set private property on non-object"); @@ -1214,7 +1215,7 @@ impl Context { if let Some(object) = object.as_object() { let mut object_borrow_mut = object.borrow_mut(); object_borrow_mut - .set_private_element(name, PrivateElement::Method(value.clone())); + .set_private_element(name.sym(), PrivateElement::Method(value.clone())); } else { return self.throw_type_error("cannot set private setter on non-object"); } @@ -1227,7 +1228,7 @@ impl Context { let object = self.vm.pop(); if let Some(object) = object.as_object() { let mut object_borrow_mut = object.borrow_mut(); - object_borrow_mut.set_private_element_setter(name, value.clone()); + object_borrow_mut.set_private_element_setter(name.sym(), value.clone()); } else { return self.throw_type_error("cannot set private setter on non-object"); } @@ -1240,7 +1241,7 @@ impl Context { let object = self.vm.pop(); if let Some(object) = object.as_object() { let mut object_borrow_mut = object.borrow_mut(); - object_borrow_mut.set_private_element_getter(name, value.clone()); + object_borrow_mut.set_private_element_getter(name.sym(), value.clone()); } else { return self.throw_type_error("cannot set private getter on non-object"); } @@ -1251,7 +1252,7 @@ impl Context { let value = self.vm.pop(); if let Some(object) = value.as_object() { let object_borrow_mut = object.borrow(); - if let Some(element) = object_borrow_mut.get_private_element(name) { + if let Some(element) = object_borrow_mut.get_private_element(name.sym()) { match element { PrivateElement::Field(value) => self.vm.push(value), PrivateElement::Method(method) => self.vm.push(method.clone()), @@ -1323,7 +1324,7 @@ impl Context { .as_function_mut() .expect("class must be function object") .push_field_private( - name, + name.sym(), JsFunction::from_object_unchecked(field_function_object.clone()), ); } @@ -1340,7 +1341,7 @@ impl Context { .as_function_mut() .expect("class must be function object") .push_private_method( - name, + name.sym(), PrivateElement::Accessor { getter: Some(getter_object.clone()), setter: None, @@ -1360,7 +1361,7 @@ impl Context { .as_function_mut() .expect("class must be function object") .push_private_method( - name, + name.sym(), PrivateElement::Accessor { getter: None, setter: Some(setter_object.clone()), @@ -1379,14 +1380,14 @@ impl Context { .borrow_mut() .as_function_mut() .expect("class must be function object") - .push_private_method(name, PrivateElement::Method(method_object.clone())); + .push_private_method(name.sym(), PrivateElement::Method(method_object.clone())); } Opcode::DeletePropertyByName => { let index = self.vm.read::(); let key = self.vm.frame().code.names[index as usize]; let key = self .interner() - .resolve_expect(key) + .resolve_expect(key.sym()) .into_common::(false) .into(); let object = self.vm.pop(); From 080e373ba9acc9b6af3e3351ecc451362441b15b Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sat, 1 Oct 2022 01:38:30 -0500 Subject: [PATCH 5/8] Fix last bugs --- boa_engine/src/bytecompiler/mod.rs | 12 ++++++++++ .../src/syntax/ast/statement/iteration/mod.rs | 6 ++++- boa_engine/src/syntax/parser/mod.rs | 2 +- .../statement/iteration/for_statement.rs | 24 ++++++++++++++++++- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index a7bbcc3a1d2..54c772ee59f 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -1657,6 +1657,12 @@ impl<'b> ByteCompiler<'b> { self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } }, + IterableLoopInitializer::Pattern(pattern) => { + for ident in pattern.idents() { + self.context.create_mutable_binding(ident, true); + } + self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; + } } self.compile_stmt(for_in_loop.body(), false)?; @@ -1767,6 +1773,12 @@ impl<'b> ByteCompiler<'b> { self.compile_declaration_pattern(pattern, BindingOpcode::InitConst)?; } }, + IterableLoopInitializer::Pattern(pattern) => { + for ident in pattern.idents() { + self.context.create_mutable_binding(ident, true); + } + self.compile_declaration_pattern(pattern, BindingOpcode::InitVar)?; + } } self.compile_stmt(for_of_loop.body(), false)?; diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_engine/src/syntax/ast/statement/iteration/mod.rs index 9d262684327..f57a20ce972 100644 --- a/boa_engine/src/syntax/ast/statement/iteration/mod.rs +++ b/boa_engine/src/syntax/ast/statement/iteration/mod.rs @@ -8,7 +8,7 @@ pub mod for_loop; pub mod for_of_loop; pub mod while_loop; -use crate::syntax::ast::expression::Identifier; +use crate::syntax::ast::{expression::Identifier, pattern::Pattern}; pub use self::{ do_while_loop::DoWhileLoop, for_in_loop::ForInLoop, for_loop::ForLoop, for_of_loop::ForOfLoop, @@ -29,6 +29,7 @@ pub enum IterableLoopInitializer { Var(Binding), Let(Binding), Const(Binding), + Pattern(Pattern), } impl IterableLoopInitializer { @@ -52,6 +53,7 @@ impl IterableLoopInitializer { match self { Self::Identifier(ident) => *ident == Sym::ARGUMENTS, Self::Var(bind) | Self::Let(bind) | Self::Const(bind) => bind.contains_arguments(), + Self::Pattern(pattern) => pattern.contains_arguments(), } } @@ -61,6 +63,7 @@ impl IterableLoopInitializer { Self::Var(declaration) | Self::Let(declaration) | Self::Const(declaration) => { declaration.contains(symbol) } + Self::Pattern(pattern) => pattern.contains(symbol), Self::Identifier(_) => false, } } @@ -70,6 +73,7 @@ impl ToInternedString for IterableLoopInitializer { fn to_interned_string(&self, interner: &Interner) -> String { let (binding, pre) = match self { Self::Identifier(ident) => return ident.to_interned_string(interner), + Self::Pattern(pattern) => return pattern.to_interned_string(interner), Self::Var(binding) => (binding, "var"), Self::Let(binding) => (binding, "let"), Self::Const(binding) => (binding, "const"), diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs index 2b0ca4df6dc..04665dffd57 100644 --- a/boa_engine/src/syntax/parser/mod.rs +++ b/boa_engine/src/syntax/parser/mod.rs @@ -219,7 +219,7 @@ impl Parser { Position::new(1, 1), )); } - if !flags.in_class_field_initializer && body.contains_arguments() { + if flags.in_class_field_initializer && body.contains_arguments() { return Err(ParseError::general( "invalid `arguments` reference inside eval", Position::new(1, 1), 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 a39625cec7a..73ee0e3f7b3 100644 --- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs @@ -10,6 +10,9 @@ use crate::syntax::{ ast::{ self, + expression::operator::assign::{ + array_decl_to_declaration_pattern, object_decl_to_declaration_pattern, + }, statement::{ declaration::DeclarationList, iteration::{for_loop::ForLoopInitializer, IterableLoopInitializer}, @@ -345,8 +348,27 @@ fn initializer_to_iterable_loop_initializer( position, ))) } - 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())) + } // TODO: implement member initializers ast::Expression::PropertyAccess(_) | ast::Expression::SuperPropertyAccess(_) From 524082f7c412dce2a24544c5684ae647c7631bc9 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sat, 1 Oct 2022 13:07:58 -0500 Subject: [PATCH 6/8] Remove solved TODO --- boa_engine/src/syntax/ast/statement/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_engine/src/syntax/ast/statement/mod.rs index 18db6375979..477de7697a8 100644 --- a/boa_engine/src/syntax/ast/statement/mod.rs +++ b/boa_engine/src/syntax/ast/statement/mod.rs @@ -36,7 +36,6 @@ use super::{ ContainsSymbol, }; -// TODO: This should be split into Expression and Statement. #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq)] pub enum Statement { From c837d442b98a5cdd7a0b001b943c23cd62f754cd Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sun, 2 Oct 2022 11:12:43 -0500 Subject: [PATCH 7/8] Apply review pt. 1 --- boa_engine/src/lib.rs | 2 +- boa_engine/src/syntax/ast/expression/access.rs | 4 ++++ boa_engine/src/syntax/ast/expression/await.rs | 1 + boa_engine/src/syntax/ast/expression/call.rs | 3 +++ .../src/syntax/ast/expression/literal/object/mod.rs | 13 +++++++------ 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 4b3c44034e0..e180ee667c2 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -35,6 +35,7 @@ clippy::needless_pass_by_value, clippy::match_wildcard_for_single_variants, clippy::map_unwrap_or, + clippy::missing_inline_in_public_items, unused_qualifications, unused_import_braces, unused_lifetimes, @@ -61,7 +62,6 @@ clippy::missing_panics_doc, clippy::too_many_lines, clippy::unreadable_literal, - clippy::missing_inline_in_public_items, clippy::cognitive_complexity, clippy::must_use_candidate, clippy::missing_errors_doc, diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_engine/src/syntax/ast/expression/access.rs index 4fa8aea6d80..ce6fdec6e43 100644 --- a/boa_engine/src/syntax/ast/expression/access.rs +++ b/boa_engine/src/syntax/ast/expression/access.rs @@ -134,6 +134,7 @@ pub struct PrivatePropertyAccess { impl PrivatePropertyAccess { /// Creates a `GetPrivateField` AST Expression. + #[inline] pub fn new(value: Expression, field: Sym) -> Self { Self { target: value.into(), @@ -142,14 +143,17 @@ impl PrivatePropertyAccess { } /// Gets the original object from where to get the field from. + #[inline] pub fn target(&self) -> &Expression { &self.target } /// Gets the name of the field to retrieve. + #[inline] pub fn field(&self) -> Sym { self.field } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.target.contains_arguments() diff --git a/boa_engine/src/syntax/ast/expression/await.rs b/boa_engine/src/syntax/ast/expression/await.rs index 0e49c4c6a32..d49132c948a 100644 --- a/boa_engine/src/syntax/ast/expression/await.rs +++ b/boa_engine/src/syntax/ast/expression/await.rs @@ -22,6 +22,7 @@ pub struct Await { impl Await { /// Return the expression that should be awaited. + #[inline] pub(crate) fn expr(&self) -> &Expression { &self.expr } diff --git a/boa_engine/src/syntax/ast/expression/call.rs b/boa_engine/src/syntax/ast/expression/call.rs index eac8c0245dd..77d6efee35e 100644 --- a/boa_engine/src/syntax/ast/expression/call.rs +++ b/boa_engine/src/syntax/ast/expression/call.rs @@ -26,6 +26,7 @@ pub struct Call { impl Call { /// Creates a new `Call` AST Expression. + #[inline] pub fn new(target: Expression, args: Box<[Expression]>) -> Self { Self { target: target.into(), @@ -34,11 +35,13 @@ impl Call { } /// Gets the name of the function call. + #[inline] pub fn expr(&self) -> &Expression { &self.target } /// Retrieves the arguments passed to the function. + #[inline] pub fn args(&self) -> &[Expression] { &self.args } diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs index 82dfe4289e4..da9690c9f18 100644 --- a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs +++ b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs @@ -3,12 +3,13 @@ #[cfg(test)] mod tests; -use crate::syntax::ast::expression::Expression; -use crate::syntax::ast::ContainsSymbol; - -use crate::syntax::ast::property::MethodDefinition; -use crate::syntax::ast::property::PropertyDefinition; -use crate::syntax::ast::{block_to_string, join_nodes}; +use crate::syntax::ast::{ + block_to_string, + expression::Expression, + join_nodes, + property::{MethodDefinition, PropertyDefinition}, + ContainsSymbol, +}; use boa_interner::{Interner, ToInternedString}; /// Objects in JavaScript may be defined as an unordered collection of related data, of From 34b00b09a5888fdcec629078f272d1ac1bbcbdcb Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Sun, 2 Oct 2022 11:36:32 -0500 Subject: [PATCH 8/8] Apply review pt.2 --- boa_engine/src/lib.rs | 2 +- boa_engine/src/syntax/ast/expression/access.rs | 12 ++++++++++++ boa_engine/src/syntax/ast/expression/await.rs | 3 +++ boa_engine/src/syntax/ast/expression/call.rs | 4 ++++ boa_engine/src/syntax/ast/expression/identifier.rs | 7 +++++++ .../src/syntax/ast/expression/literal/array.rs | 4 ++++ boa_engine/src/syntax/ast/expression/literal/mod.rs | 8 ++++++++ .../src/syntax/ast/expression/literal/object/mod.rs | 4 ++++ .../src/syntax/ast/expression/literal/template.rs | 3 +++ boa_engine/src/syntax/ast/expression/mod.rs | 3 +++ boa_engine/src/syntax/ast/expression/new.rs | 5 +++++ .../src/syntax/ast/expression/operator/assign/mod.rs | 7 +++++++ .../src/syntax/ast/expression/operator/assign/op.rs | 1 + .../src/syntax/ast/expression/operator/binary/mod.rs | 5 +++++ .../src/syntax/ast/expression/operator/binary/op.rs | 9 +++++++++ .../syntax/ast/expression/operator/conditional.rs | 6 ++++++ .../src/syntax/ast/expression/operator/unary/mod.rs | 4 ++++ .../src/syntax/ast/expression/operator/unary/op.rs | 1 + boa_engine/src/syntax/ast/expression/spread.rs | 4 ++++ .../src/syntax/ast/expression/tagged_template.rs | 8 ++++++++ boa_engine/src/syntax/ast/expression/yield.rs | 5 +++++ 21 files changed, 104 insertions(+), 1 deletion(-) diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index e180ee667c2..7a0f0e030cd 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -35,7 +35,6 @@ clippy::needless_pass_by_value, clippy::match_wildcard_for_single_variants, clippy::map_unwrap_or, - clippy::missing_inline_in_public_items, unused_qualifications, unused_import_braces, unused_lifetimes, @@ -53,6 +52,7 @@ nonstandard_style, )] #![allow( + clippy::missing_inline_in_public_items, clippy::module_name_repetitions, clippy::cast_possible_truncation, clippy::cast_sign_loss, diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_engine/src/syntax/ast/expression/access.rs index ce6fdec6e43..9ce723a450d 100644 --- a/boa_engine/src/syntax/ast/expression/access.rs +++ b/boa_engine/src/syntax/ast/expression/access.rs @@ -25,12 +25,14 @@ impl PropertyAccessField { } impl From for PropertyAccessField { + #[inline] fn from(id: Sym) -> Self { Self::Const(id) } } impl From for PropertyAccessField { + #[inline] fn from(expr: Expression) -> Self { Self::Expr(Box::new(expr)) } @@ -66,15 +68,18 @@ pub struct PropertyAccess { } impl PropertyAccess { + #[inline] pub fn target(&self) -> &Expression { &self.target } + #[inline] pub fn field(&self) -> &PropertyAccessField { &self.field } /// Creates a `PropertyAccess` AST Expression. + #[inline] pub fn new(target: Expression, field: F) -> Self where F: Into, @@ -97,6 +102,7 @@ impl PropertyAccess { } impl ToInternedString for PropertyAccess { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let target = self.target.to_interned_string(interner); match self.field { @@ -109,6 +115,7 @@ impl ToInternedString for PropertyAccess { } impl From for Expression { + #[inline] fn from(access: PropertyAccess) -> Self { Self::PropertyAccess(access) } @@ -166,6 +173,7 @@ impl PrivatePropertyAccess { } impl ToInternedString for PrivatePropertyAccess { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!( "{}.#{}", @@ -176,6 +184,7 @@ impl ToInternedString for PrivatePropertyAccess { } impl From for Expression { + #[inline] fn from(access: PrivatePropertyAccess) -> Self { Self::PrivatePropertyAccess(access) } @@ -201,6 +210,7 @@ impl SuperPropertyAccess { } /// Gets the name of the field to retrieve. + #[inline] pub fn field(&self) -> &PropertyAccessField { &self.field } @@ -217,6 +227,7 @@ impl SuperPropertyAccess { } impl ToInternedString for SuperPropertyAccess { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { match &self.field { PropertyAccessField::Const(field) => { @@ -230,6 +241,7 @@ impl ToInternedString for SuperPropertyAccess { } impl From for Expression { + #[inline] fn from(access: SuperPropertyAccess) -> Self { Self::SuperPropertyAccess(access) } diff --git a/boa_engine/src/syntax/ast/expression/await.rs b/boa_engine/src/syntax/ast/expression/await.rs index d49132c948a..e6964ff8119 100644 --- a/boa_engine/src/syntax/ast/expression/await.rs +++ b/boa_engine/src/syntax/ast/expression/await.rs @@ -42,18 +42,21 @@ impl From for Await where T: Into>, { + #[inline] fn from(e: T) -> Self { Self { expr: e.into() } } } impl ToInternedString for Await { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!("await {}", self.expr.to_indented_string(interner, 0)) } } impl From for Expression { + #[inline] fn from(awaitexpr: Await) -> Self { Self::Await(awaitexpr) } diff --git a/boa_engine/src/syntax/ast/expression/call.rs b/boa_engine/src/syntax/ast/expression/call.rs index 77d6efee35e..51d3ecf0b9c 100644 --- a/boa_engine/src/syntax/ast/expression/call.rs +++ b/boa_engine/src/syntax/ast/expression/call.rs @@ -58,6 +58,7 @@ impl Call { } impl ToInternedString for Call { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!( "{}({})", @@ -68,6 +69,7 @@ impl ToInternedString for Call { } impl From for Expression { + #[inline] fn from(call: Call) -> Self { Self::Call(call) } @@ -113,12 +115,14 @@ impl SuperCall { } impl ToInternedString for SuperCall { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!("super({})", join_nodes(interner, &self.args)) } } impl From for Expression { + #[inline] fn from(call: SuperCall) -> Self { Self::SuperCall(call) } diff --git a/boa_engine/src/syntax/ast/expression/identifier.rs b/boa_engine/src/syntax/ast/expression/identifier.rs index 2a89b48d3c3..2efdeac9f24 100644 --- a/boa_engine/src/syntax/ast/expression/identifier.rs +++ b/boa_engine/src/syntax/ast/expression/identifier.rs @@ -33,12 +33,14 @@ pub struct Identifier { } impl PartialEq for Identifier { + #[inline] fn eq(&self, other: &Sym) -> bool { self.ident == *other } } impl PartialEq for Sym { + #[inline] fn eq(&self, other: &Identifier) -> bool { *self == other.ident } @@ -46,11 +48,13 @@ impl PartialEq for Sym { impl Identifier { /// Creates a new identifier AST Expression. + #[inline] pub fn new(ident: Sym) -> Self { Self { ident } } /// Retrieves the identifier's string symbol in the interner. + #[inline] pub fn sym(self) -> Sym { self.ident } @@ -75,6 +79,7 @@ impl Identifier { } impl ToInternedString for Identifier { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { interner.resolve_expect(self.ident).join( String::from, @@ -85,12 +90,14 @@ impl ToInternedString for Identifier { } impl From for Identifier { + #[inline] fn from(sym: Sym) -> Self { Self { ident: sym } } } impl From for Expression { + #[inline] fn from(local: Identifier) -> Self { Self::Identifier(local) } diff --git a/boa_engine/src/syntax/ast/expression/literal/array.rs b/boa_engine/src/syntax/ast/expression/literal/array.rs index 8516359521b..76c3977f3db 100644 --- a/boa_engine/src/syntax/ast/expression/literal/array.rs +++ b/boa_engine/src/syntax/ast/expression/literal/array.rs @@ -59,6 +59,7 @@ impl ArrayLiteral { } impl AsRef<[Option]> for ArrayLiteral { + #[inline] fn as_ref(&self) -> &[Option] { &self.arr } @@ -68,6 +69,7 @@ impl From for ArrayLiteral where T: Into]>>, { + #[inline] fn from(decl: T) -> Self { Self { arr: decl.into(), @@ -77,6 +79,7 @@ where } impl ToInternedString for ArrayLiteral { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = String::from("["); let mut first = true; @@ -96,6 +99,7 @@ impl ToInternedString for ArrayLiteral { } impl From for Expression { + #[inline] fn from(arr: ArrayLiteral) -> Self { Self::ArrayLiteral(arr) } diff --git a/boa_engine/src/syntax/ast/expression/literal/mod.rs b/boa_engine/src/syntax/ast/expression/literal/mod.rs index 3a5c8125b3c..a809b369e82 100644 --- a/boa_engine/src/syntax/ast/expression/literal/mod.rs +++ b/boa_engine/src/syntax/ast/expression/literal/mod.rs @@ -120,48 +120,56 @@ pub enum Literal { } impl From for Literal { + #[inline] fn from(string: Sym) -> Self { Self::String(string) } } impl From for Literal { + #[inline] fn from(num: f64) -> Self { Self::Num(num) } } impl From for Literal { + #[inline] fn from(i: i32) -> Self { Self::Int(i) } } impl From for Literal { + #[inline] fn from(i: BigInt) -> Self { Self::BigInt(Box::new(i)) } } impl From> for Literal { + #[inline] fn from(i: Box) -> Self { Self::BigInt(i) } } impl From for Literal { + #[inline] fn from(b: bool) -> Self { Self::Bool(b) } } impl From for Expression { + #[inline] fn from(lit: Literal) -> Self { Expression::Literal(lit) } } impl ToInternedString for Literal { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { match *self { Self::String(st) => { diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs index da9690c9f18..020d5a466b9 100644 --- a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs +++ b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs @@ -39,6 +39,7 @@ pub struct ObjectLiteral { } impl ObjectLiteral { + #[inline] pub fn properties(&self) -> &[PropertyDefinition] { &self.properties } @@ -133,6 +134,7 @@ impl ObjectLiteral { } impl ToInternedString for ObjectLiteral { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } @@ -142,6 +144,7 @@ impl From for ObjectLiteral where T: Into>, { + #[inline] fn from(props: T) -> Self { Self { properties: props.into(), @@ -150,6 +153,7 @@ where } impl From for Expression { + #[inline] fn from(obj: ObjectLiteral) -> Self { Self::ObjectLiteral(obj) } diff --git a/boa_engine/src/syntax/ast/expression/literal/template.rs b/boa_engine/src/syntax/ast/expression/literal/template.rs index ea2002fe4ef..57a0d0e0c5a 100644 --- a/boa_engine/src/syntax/ast/expression/literal/template.rs +++ b/boa_engine/src/syntax/ast/expression/literal/template.rs @@ -24,6 +24,7 @@ pub struct TemplateLiteral { } impl From for Expression { + #[inline] fn from(tem: TemplateLiteral) -> Self { Self::TemplateLiteral(tem) } @@ -37,6 +38,7 @@ pub enum TemplateElement { } impl TemplateLiteral { + #[inline] pub fn new(elements: Box<[TemplateElement]>) -> Self { Self { elements } } @@ -63,6 +65,7 @@ impl TemplateLiteral { } impl ToInternedString for TemplateLiteral { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = "`".to_owned(); diff --git a/boa_engine/src/syntax/ast/expression/mod.rs b/boa_engine/src/syntax/ast/expression/mod.rs index 817948792ef..c9584ab41d4 100644 --- a/boa_engine/src/syntax/ast/expression/mod.rs +++ b/boa_engine/src/syntax/ast/expression/mod.rs @@ -143,6 +143,7 @@ pub enum Expression { impl Expression { /// Creates a string of the value of the expression with the given indentation. + #[inline] pub fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { self.to_no_indent_string(interner, indentation) } @@ -274,12 +275,14 @@ impl Expression { } impl From for Statement { + #[inline] fn from(expr: Expression) -> Self { Statement::Expression(expr) } } impl ToInternedString for Expression { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { self.to_indented_string(interner, 0) } diff --git a/boa_engine/src/syntax/ast/expression/new.rs b/boa_engine/src/syntax/ast/expression/new.rs index 849faf2654f..7f7a88c6a0b 100644 --- a/boa_engine/src/syntax/ast/expression/new.rs +++ b/boa_engine/src/syntax/ast/expression/new.rs @@ -26,11 +26,13 @@ pub struct New { impl New { /// Gets the name of the function call. + #[inline] pub fn expr(&self) -> &Expression { self.call.expr() } /// Retrieves the arguments passed to the function. + #[inline] pub fn args(&self) -> &[Expression] { self.call.args() } @@ -52,18 +54,21 @@ impl New { } impl From for New { + #[inline] fn from(call: Call) -> Self { Self { call } } } impl ToInternedString for New { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!("new {}", self.call.to_interned_string(interner)) } } impl From for Expression { + #[inline] fn from(new: New) -> Self { Self::New(new) } diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs index 3547f6e67cb..7a12785654c 100644 --- a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs @@ -49,16 +49,19 @@ impl Assign { } /// Gets the operator of the assignment operation. + #[inline] pub fn op(&self) -> op::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 } @@ -87,6 +90,7 @@ impl Assign { } impl ToInternedString for Assign { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!( "{} {} {}", @@ -98,6 +102,7 @@ impl ToInternedString for Assign { } impl From for Expression { + #[inline] fn from(op: Assign) -> Self { Self::Assign(op) } @@ -148,6 +153,7 @@ impl AssignTarget { } impl ToInternedString for AssignTarget { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { match self { AssignTarget::Identifier(id) => id.to_interned_string(interner), @@ -160,6 +166,7 @@ impl ToInternedString for AssignTarget { } impl From for AssignTarget { + #[inline] fn from(target: Identifier) -> Self { Self::Identifier(target) } diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs b/boa_engine/src/syntax/ast/expression/operator/assign/op.rs index 45f1d0e1b03..5900b2745d2 100644 --- a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs +++ b/boa_engine/src/syntax/ast/expression/operator/assign/op.rs @@ -236,6 +236,7 @@ impl AssignOp { } impl std::fmt::Display for AssignOp { + #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) } diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs index b045a80a28b..b90689c99a8 100644 --- a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs @@ -29,16 +29,19 @@ impl Binary { } /// Gets the binary operation of the Expression. + #[inline] pub fn op(&self) -> op::BinaryOp { self.op } /// Gets the left hand side of the binary operation. + #[inline] pub fn lhs(&self) -> &Expression { &self.lhs } /// Gets the right hand side of the binary operation. + #[inline] pub fn rhs(&self) -> &Expression { &self.rhs } @@ -55,6 +58,7 @@ impl Binary { } impl ToInternedString for Binary { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!( "{} {} {}", @@ -66,6 +70,7 @@ impl ToInternedString for Binary { } impl From for Expression { + #[inline] fn from(op: Binary) -> Self { Self::Binary(op) } diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs b/boa_engine/src/syntax/ast/expression/operator/binary/op.rs index f4cc00b2c59..cb6739b5f3d 100644 --- a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs +++ b/boa_engine/src/syntax/ast/expression/operator/binary/op.rs @@ -31,24 +31,28 @@ pub enum BinaryOp { } impl From for BinaryOp { + #[inline] fn from(op: ArithmeticOp) -> Self { Self::Arithmetic(op) } } impl From for BinaryOp { + #[inline] fn from(op: BitwiseOp) -> Self { Self::Bitwise(op) } } impl From for BinaryOp { + #[inline] fn from(op: RelationalOp) -> Self { Self::Relational(op) } } impl From for BinaryOp { + #[inline] fn from(op: LogicalOp) -> Self { Self::Logical(op) } @@ -68,6 +72,7 @@ impl BinaryOp { } impl Display for BinaryOp { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.as_str()) } @@ -177,6 +182,7 @@ impl ArithmeticOp { } impl Display for ArithmeticOp { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.as_str()) } @@ -291,6 +297,7 @@ impl BitwiseOp { } impl Display for BitwiseOp { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.as_str()) } @@ -487,6 +494,7 @@ impl RelationalOp { } impl Display for RelationalOp { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.as_str()) } @@ -558,6 +566,7 @@ impl LogicalOp { } impl Display for LogicalOp { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{}", self.as_str()) } diff --git a/boa_engine/src/syntax/ast/expression/operator/conditional.rs b/boa_engine/src/syntax/ast/expression/operator/conditional.rs index 886466ff37b..b32d9291e73 100644 --- a/boa_engine/src/syntax/ast/expression/operator/conditional.rs +++ b/boa_engine/src/syntax/ast/expression/operator/conditional.rs @@ -24,19 +24,23 @@ pub struct Conditional { } impl Conditional { + #[inline] pub fn cond(&self) -> &Expression { &self.condition } + #[inline] pub fn if_true(&self) -> &Expression { &self.if_true } + #[inline] pub fn if_false(&self) -> &Expression { &self.if_false } /// Creates a `ConditionalOp` AST Expression. + #[inline] pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self { Self { condition: Box::new(condition), @@ -61,6 +65,7 @@ impl Conditional { } impl ToInternedString for Conditional { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!( "{} ? {} : {}", @@ -72,6 +77,7 @@ impl ToInternedString for Conditional { } impl From for Expression { + #[inline] fn from(cond_op: Conditional) -> Self { Self::Conditional(cond_op) } diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs index 216ac51fee7..0f43bc2b7c8 100644 --- a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs +++ b/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs @@ -29,11 +29,13 @@ impl Unary { } /// Gets the unary operation of the Expression. + #[inline] pub fn op(&self) -> op::UnaryOp { self.op } /// Gets the target of this unary operator. + #[inline] pub fn target(&self) -> &Expression { self.target.as_ref() } @@ -50,6 +52,7 @@ impl Unary { } impl ToInternedString for Unary { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let space = match self.op { op::UnaryOp::TypeOf | op::UnaryOp::Delete | op::UnaryOp::Void => " ", @@ -64,6 +67,7 @@ impl ToInternedString for Unary { } impl From for Expression { + #[inline] fn from(op: Unary) -> Self { Self::Unary(op) } diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs b/boa_engine/src/syntax/ast/expression/operator/unary/op.rs index 76c53d26ca6..a14a634d550 100644 --- a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs +++ b/boa_engine/src/syntax/ast/expression/operator/unary/op.rs @@ -208,6 +208,7 @@ impl UnaryOp { } impl std::fmt::Display for UnaryOp { + #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) } diff --git a/boa_engine/src/syntax/ast/expression/spread.rs b/boa_engine/src/syntax/ast/expression/spread.rs index 13ce9d83776..63082288759 100644 --- a/boa_engine/src/syntax/ast/expression/spread.rs +++ b/boa_engine/src/syntax/ast/expression/spread.rs @@ -28,11 +28,13 @@ pub struct Spread { } impl Spread { + #[inline] pub fn val(&self) -> &Expression { &self.val } /// Creates a `Spread` AST Expression. + #[inline] pub fn new(val: Expression) -> Self { Self { val: Box::new(val) } } @@ -49,12 +51,14 @@ impl Spread { } impl ToInternedString for Spread { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { format!("...{}", self.val().to_interned_string(interner)) } } impl From for Expression { + #[inline] fn from(spread: Spread) -> Self { Self::Spread(spread) } diff --git a/boa_engine/src/syntax/ast/expression/tagged_template.rs b/boa_engine/src/syntax/ast/expression/tagged_template.rs index 887dc845dea..7f83218dfdc 100644 --- a/boa_engine/src/syntax/ast/expression/tagged_template.rs +++ b/boa_engine/src/syntax/ast/expression/tagged_template.rs @@ -16,6 +16,7 @@ pub struct TaggedTemplate { impl TaggedTemplate { /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and /// the expressions. + #[inline] pub fn new( tag: Expression, raws: Box<[Sym]>, @@ -30,22 +31,27 @@ impl TaggedTemplate { } } + #[inline] pub(crate) fn tag(&self) -> &Expression { &self.tag } + #[inline] pub(crate) fn raws(&self) -> &[Sym] { &self.raws } + #[inline] pub(crate) fn cookeds(&self) -> &[Option] { &self.cookeds } + #[inline] pub(crate) fn exprs(&self) -> &[Expression] { &self.exprs } + #[inline] pub(crate) fn contains_arguments(&self) -> bool { self.tag.contains_arguments() || self.exprs.iter().any(Expression::contains_arguments) } @@ -57,6 +63,7 @@ impl TaggedTemplate { } impl ToInternedString for TaggedTemplate { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = format!("{}`", self.tag.to_interned_string(interner)); for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { @@ -73,6 +80,7 @@ impl ToInternedString for TaggedTemplate { } impl From for Expression { + #[inline] fn from(template: TaggedTemplate) -> Self { Self::TaggedTemplate(template) } diff --git a/boa_engine/src/syntax/ast/expression/yield.rs b/boa_engine/src/syntax/ast/expression/yield.rs index 2f028cb7ee8..79c1e255ba2 100644 --- a/boa_engine/src/syntax/ast/expression/yield.rs +++ b/boa_engine/src/syntax/ast/expression/yield.rs @@ -20,15 +20,18 @@ pub struct Yield { } impl Yield { + #[inline] pub fn expr(&self) -> Option<&Expression> { self.expr.as_ref().map(Box::as_ref) } + #[inline] pub fn delegate(&self) -> bool { self.delegate } /// Creates a `Yield` AST Expression. + #[inline] pub fn new(expr: Option, delegate: bool) -> Self { Self { expr: expr.map(Box::new), @@ -48,12 +51,14 @@ impl Yield { } impl From for Expression { + #[inline] fn from(r#yield: Yield) -> Self { Self::Yield(r#yield) } } impl ToInternedString for Yield { + #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let y = if self.delegate { "yield*" } else { "yield" }; if let Some(ex) = self.expr() {