Browse Source

Refactor ast function types (#3931)

* Refactor AST function types

* fix lints
pull/3956/head
raskad 3 months ago committed by GitHub
parent
commit
424896dbff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 46
      core/ast/src/declaration/export.rs
  2. 61
      core/ast/src/declaration/mod.rs
  3. 11
      core/ast/src/declaration/variable.rs
  4. 4
      core/ast/src/expression/literal/array.rs
  5. 2
      core/ast/src/expression/literal/mod.rs
  6. 296
      core/ast/src/expression/literal/object.rs
  7. 108
      core/ast/src/expression/mod.rs
  8. 2
      core/ast/src/expression/parenthesized.rs
  9. 2
      core/ast/src/function/arrow_function.rs
  10. 2
      core/ast/src/function/async_arrow_function.rs
  11. 144
      core/ast/src/function/async_function.rs
  12. 135
      core/ast/src/function/async_generator.rs
  13. 712
      core/ast/src/function/class.rs
  14. 145
      core/ast/src/function/generator.rs
  15. 203
      core/ast/src/function/mod.rs
  16. 217
      core/ast/src/function/ordinary_function.rs
  17. 48
      core/ast/src/module_item_list/mod.rs
  18. 634
      core/ast/src/operations.rs
  19. 280
      core/ast/src/property.rs
  20. 21
      core/ast/src/statement/labelled.rs
  21. 2
      core/ast/src/statement/mod.rs
  22. 109
      core/ast/src/visitor.rs
  23. 14
      core/engine/src/builtins/function/mod.rs
  24. 464
      core/engine/src/bytecompiler/class.rs
  25. 122
      core/engine/src/bytecompiler/declarations.rs
  26. 12
      core/engine/src/bytecompiler/expression/mod.rs
  27. 119
      core/engine/src/bytecompiler/expression/object_literal.rs
  28. 140
      core/engine/src/bytecompiler/mod.rs
  29. 18
      core/engine/src/bytecompiler/module.rs
  30. 2
      core/engine/src/bytecompiler/statement/labelled.rs
  31. 10
      core/engine/src/module/source.rs
  32. 17
      core/engine/src/object/operations.rs
  33. 38
      core/engine/src/tests/class.rs
  34. 1
      core/engine/src/tests/mod.rs
  35. 6
      core/engine/src/vm/code_block.rs
  36. 2
      core/engine/src/vm/flowgraph/mod.rs
  37. 4
      core/engine/src/vm/opcode/mod.rs
  38. 8
      core/engine/src/vm/opcode/push/class/field.rs
  39. 16
      core/parser/src/parser/expression/assignment/arrow_function.rs
  40. 10
      core/parser/src/parser/expression/assignment/async_arrow_function.rs
  41. 32
      core/parser/src/parser/expression/assignment/conditional.rs
  42. 22
      core/parser/src/parser/expression/assignment/exponentiation.rs
  43. 55
      core/parser/src/parser/expression/assignment/mod.rs
  44. 4
      core/parser/src/parser/expression/assignment/yield.rs
  45. 2
      core/parser/src/parser/expression/await_expr.rs
  46. 4
      core/parser/src/parser/expression/left_hand_side/arguments.rs
  47. 2
      core/parser/src/parser/expression/left_hand_side/call.rs
  48. 13
      core/parser/src/parser/expression/left_hand_side/member.rs
  49. 11
      core/parser/src/parser/expression/left_hand_side/mod.rs
  50. 2
      core/parser/src/parser/expression/left_hand_side/optional/mod.rs
  51. 2
      core/parser/src/parser/expression/left_hand_side/template.rs
  52. 121
      core/parser/src/parser/expression/mod.rs
  53. 10
      core/parser/src/parser/expression/primary/array_initializer/mod.rs
  54. 18
      core/parser/src/parser/expression/primary/async_function_expression/mod.rs
  55. 8
      core/parser/src/parser/expression/primary/async_function_expression/tests.rs
  56. 18
      core/parser/src/parser/expression/primary/async_generator_expression/mod.rs
  57. 8
      core/parser/src/parser/expression/primary/async_generator_expression/tests.rs
  58. 28
      core/parser/src/parser/expression/primary/class_expression/mod.rs
  59. 19
      core/parser/src/parser/expression/primary/function_expression/mod.rs
  60. 13
      core/parser/src/parser/expression/primary/function_expression/tests.rs
  61. 18
      core/parser/src/parser/expression/primary/generator_expression/mod.rs
  62. 6
      core/parser/src/parser/expression/primary/generator_expression/tests.rs
  63. 26
      core/parser/src/parser/expression/primary/mod.rs
  64. 299
      core/parser/src/parser/expression/primary/object_initializer/mod.rs
  65. 93
      core/parser/src/parser/expression/primary/object_initializer/tests.rs
  66. 4
      core/parser/src/parser/expression/primary/template/mod.rs
  67. 9
      core/parser/src/parser/expression/unary.rs
  68. 20
      core/parser/src/parser/expression/update.rs
  69. 10
      core/parser/src/parser/function/mod.rs
  70. 34
      core/parser/src/parser/function/tests.rs
  71. 12
      core/parser/src/parser/statement/block/tests.rs
  72. 12
      core/parser/src/parser/statement/declaration/export.rs
  73. 10
      core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  74. 47
      core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs
  75. 13
      core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
  76. 17
      core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
  77. 692
      core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs
  78. 45
      core/parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs
  79. 11
      core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs
  80. 24
      core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs
  81. 6
      core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs
  82. 7
      core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs
  83. 18
      core/parser/src/parser/statement/declaration/lexical.rs
  84. 4
      core/parser/src/parser/statement/expression/mod.rs
  85. 16
      core/parser/src/parser/statement/if_stm/mod.rs
  86. 4
      core/parser/src/parser/statement/iteration/do_while_statement.rs
  87. 12
      core/parser/src/parser/statement/iteration/for_statement.rs
  88. 4
      core/parser/src/parser/statement/iteration/while_statement.rs
  89. 34
      core/parser/src/parser/statement/mod.rs
  90. 4
      core/parser/src/parser/statement/return_stm/mod.rs
  91. 6
      core/parser/src/parser/statement/switch/mod.rs
  92. 4
      core/parser/src/parser/statement/throw/mod.rs
  93. 18
      core/parser/src/parser/statement/variable/mod.rs
  94. 4
      core/parser/src/parser/statement/with/mod.rs
  95. 12
      core/parser/src/parser/tests/mod.rs

46
core/ast/src/declaration/export.rs

@ -9,17 +9,19 @@
//! [spec]: https://tc39.es/ecma262/#sec-exports
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
use std::ops::ControlFlow;
use super::{ModuleSpecifier, VarDeclaration};
use crate::{
expression::Identifier,
function::{AsyncFunction, AsyncGenerator, Class, Function, Generator},
function::{
AsyncFunctionDeclaration, AsyncGeneratorDeclaration, ClassDeclaration, FunctionDeclaration,
GeneratorDeclaration,
},
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Declaration, Expression,
};
use boa_interner::Sym;
use std::ops::ControlFlow;
/// The kind of re-export in an [`ExportDeclaration`].
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -95,15 +97,15 @@ pub enum ExportDeclaration {
/// Declaration export.
Declaration(Declaration),
/// Default function export.
DefaultFunction(Function),
DefaultFunctionDeclaration(FunctionDeclaration),
/// Default generator export.
DefaultGenerator(Generator),
DefaultGeneratorDeclaration(GeneratorDeclaration),
/// Default async function export.
DefaultAsyncFunction(AsyncFunction),
DefaultAsyncFunctionDeclaration(AsyncFunctionDeclaration),
/// Default async generator export.
DefaultAsyncGenerator(AsyncGenerator),
DefaultAsyncGeneratorDeclaration(AsyncGeneratorDeclaration),
/// Default class declaration export.
DefaultClassDeclaration(Class),
DefaultClassDeclaration(ClassDeclaration),
/// Default assignment expression export.
DefaultAssignmentExpression(Expression),
}
@ -126,11 +128,15 @@ impl VisitWith for ExportDeclaration {
}
Self::VarStatement(var) => visitor.visit_var_declaration(var),
Self::Declaration(decl) => visitor.visit_declaration(decl),
Self::DefaultFunction(f) => visitor.visit_function(f),
Self::DefaultGenerator(g) => visitor.visit_generator(g),
Self::DefaultAsyncFunction(af) => visitor.visit_async_function(af),
Self::DefaultAsyncGenerator(ag) => visitor.visit_async_generator(ag),
Self::DefaultClassDeclaration(c) => visitor.visit_class(c),
Self::DefaultFunctionDeclaration(f) => visitor.visit_function_declaration(f),
Self::DefaultGeneratorDeclaration(g) => visitor.visit_generator_declaration(g),
Self::DefaultAsyncFunctionDeclaration(af) => {
visitor.visit_async_function_declaration(af)
}
Self::DefaultAsyncGeneratorDeclaration(ag) => {
visitor.visit_async_generator_declaration(ag)
}
Self::DefaultClassDeclaration(c) => visitor.visit_class_declaration(c),
Self::DefaultAssignmentExpression(expr) => visitor.visit_expression(expr),
}
}
@ -152,11 +158,15 @@ impl VisitWith for ExportDeclaration {
}
Self::VarStatement(var) => visitor.visit_var_declaration_mut(var),
Self::Declaration(decl) => visitor.visit_declaration_mut(decl),
Self::DefaultFunction(f) => visitor.visit_function_mut(f),
Self::DefaultGenerator(g) => visitor.visit_generator_mut(g),
Self::DefaultAsyncFunction(af) => visitor.visit_async_function_mut(af),
Self::DefaultAsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Self::DefaultClassDeclaration(c) => visitor.visit_class_mut(c),
Self::DefaultFunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),
Self::DefaultGeneratorDeclaration(g) => visitor.visit_generator_declaration_mut(g),
Self::DefaultAsyncFunctionDeclaration(af) => {
visitor.visit_async_function_declaration_mut(af)
}
Self::DefaultAsyncGeneratorDeclaration(ag) => {
visitor.visit_async_generator_declaration_mut(ag)
}
Self::DefaultClassDeclaration(c) => visitor.visit_class_declaration_mut(c),
Self::DefaultAssignmentExpression(expr) => visitor.visit_expression_mut(expr),
}
}

61
core/ast/src/declaration/mod.rs

@ -14,7 +14,13 @@
//! [class]: https://tc39.es/ecma262/#prod-ClassDeclaration
//! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations
use super::function::{AsyncFunction, AsyncGenerator, Class, Function, Generator};
use super::function::{
AsyncFunctionDeclaration, AsyncGeneratorDeclaration, FunctionDeclaration, GeneratorDeclaration,
};
use crate::{
function::ClassDeclaration,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@ -22,7 +28,6 @@ mod export;
mod import;
mod variable;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
pub use export::*;
pub use import::*;
pub use variable::*;
@ -34,20 +39,20 @@ pub use variable::*;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Declaration {
/// See [`Function`]
Function(Function),
/// See [`FunctionDeclaration`]
FunctionDeclaration(FunctionDeclaration),
/// See [`Generator`]
Generator(Generator),
/// See [`GeneratorDeclaration`]
GeneratorDeclaration(GeneratorDeclaration),
/// See [`AsyncFunction`]
AsyncFunction(AsyncFunction),
/// See [`AsyncFunctionDeclaration`]
AsyncFunctionDeclaration(AsyncFunctionDeclaration),
/// See [`AsyncGenerator`]
AsyncGenerator(AsyncGenerator),
/// See [`AsyncGeneratorDeclaration`]
AsyncGeneratorDeclaration(AsyncGeneratorDeclaration),
/// See [`Class`]
Class(Class),
/// See [`ClassDeclaration`]
ClassDeclaration(ClassDeclaration),
/// See [`LexicalDeclaration`]
Lexical(LexicalDeclaration),
@ -56,11 +61,11 @@ pub enum Declaration {
impl ToIndentedString for Declaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
match self {
Self::Function(f) => f.to_indented_string(interner, indentation),
Self::Generator(g) => g.to_indented_string(interner, indentation),
Self::AsyncFunction(af) => af.to_indented_string(interner, indentation),
Self::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation),
Self::Class(c) => c.to_indented_string(interner, indentation),
Self::FunctionDeclaration(f) => f.to_indented_string(interner, indentation),
Self::GeneratorDeclaration(g) => g.to_indented_string(interner, indentation),
Self::AsyncFunctionDeclaration(af) => af.to_indented_string(interner, indentation),
Self::AsyncGeneratorDeclaration(ag) => ag.to_indented_string(interner, indentation),
Self::ClassDeclaration(c) => c.to_indented_string(interner, indentation),
Self::Lexical(l) => {
let mut s = l.to_interned_string(interner);
s.push(';');
@ -76,11 +81,11 @@ impl VisitWith for Declaration {
V: Visitor<'a>,
{
match self {
Self::Function(f) => visitor.visit_function(f),
Self::Generator(g) => visitor.visit_generator(g),
Self::AsyncFunction(af) => visitor.visit_async_function(af),
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
Self::Class(c) => visitor.visit_class(c),
Self::FunctionDeclaration(f) => visitor.visit_function_declaration(f),
Self::GeneratorDeclaration(g) => visitor.visit_generator_declaration(g),
Self::AsyncFunctionDeclaration(af) => visitor.visit_async_function_declaration(af),
Self::AsyncGeneratorDeclaration(ag) => visitor.visit_async_generator_declaration(ag),
Self::ClassDeclaration(c) => visitor.visit_class_declaration(c),
Self::Lexical(ld) => visitor.visit_lexical_declaration(ld),
}
}
@ -90,11 +95,13 @@ impl VisitWith for Declaration {
V: VisitorMut<'a>,
{
match self {
Self::Function(f) => visitor.visit_function_mut(f),
Self::Generator(g) => visitor.visit_generator_mut(g),
Self::AsyncFunction(af) => visitor.visit_async_function_mut(af),
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Self::Class(c) => visitor.visit_class_mut(c),
Self::FunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),
Self::GeneratorDeclaration(g) => visitor.visit_generator_declaration_mut(g),
Self::AsyncFunctionDeclaration(af) => visitor.visit_async_function_declaration_mut(af),
Self::AsyncGeneratorDeclaration(ag) => {
visitor.visit_async_generator_declaration_mut(ag)
}
Self::ClassDeclaration(c) => visitor.visit_class_declaration_mut(c),
Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld),
}
}

11
core/ast/src/declaration/variable.rs

@ -1,19 +1,16 @@
//! Variable related declarations.
use core::ops::ControlFlow;
use std::convert::TryFrom;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use super::Declaration;
use crate::{
expression::{Expression, Identifier},
join_nodes,
pattern::Pattern,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Statement,
};
use boa_interner::{Interner, ToInternedString};
use super::Declaration;
use core::{convert::TryFrom, ops::ControlFlow};
/// A [`var`][var] statement, also called [`VariableStatement`][varstmt] in the spec.
///

4
core/ast/src/expression/literal/array.rs

@ -105,9 +105,11 @@ impl ArrayLiteral {
}
match assign.lhs() {
AssignTarget::Identifier(ident) => {
let mut init = assign.rhs().clone();
init.set_anonymous_function_definition_name(ident);
bindings.push(ArrayPatternElement::SingleName {
ident: *ident,
default_init: Some(assign.rhs().clone()),
default_init: Some(init),
});
}
AssignTarget::Access(access) => {

2
core/ast/src/expression/literal/mod.rs

@ -13,7 +13,7 @@ mod template;
pub use array::ArrayLiteral;
use core::ops::ControlFlow;
pub use object::ObjectLiteral;
pub use object::{ObjectLiteral, ObjectMethodDefinition, PropertyDefinition};
pub use template::{TemplateElement, TemplateLiteral};
use crate::visitor::{VisitWith, Visitor, VisitorMut};

296
core/ast/src/expression/literal/object.rs

@ -4,11 +4,12 @@ use crate::{
block_to_string,
expression::{
operator::assign::{AssignOp, AssignTarget},
Expression, RESERVED_IDENTIFIERS_STRICT,
Expression, Identifier, RESERVED_IDENTIFIERS_STRICT,
},
function::{FormalParameterList, FunctionBody},
join_nodes,
pattern::{ObjectPattern, ObjectPatternElement},
property::{MethodDefinition, PropertyDefinition, PropertyName},
property::{MethodDefinitionKind, PropertyName},
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
@ -125,10 +126,12 @@ impl ObjectLiteral {
return None;
}
}
let mut init = assign.rhs().clone();
init.set_anonymous_function_definition_name(ident);
bindings.push(ObjectPatternElement::SingleName {
ident: *ident,
name: PropertyName::Literal(name),
default_init: Some(assign.rhs().clone()),
default_init: Some(init),
});
} else {
return None;
@ -182,16 +185,17 @@ impl ObjectLiteral {
return None;
}
}
PropertyDefinition::MethodDefinition(_, _) => return None,
PropertyDefinition::MethodDefinition(_) => return None,
PropertyDefinition::CoverInitializedName(ident, expr) => {
if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
return None;
}
let mut expr = expr.clone();
expr.set_anonymous_function_definition_name(ident);
bindings.push(ObjectPatternElement::SingleName {
ident: *ident,
name: PropertyName::Literal(ident.sym()),
default_init: Some(expr.clone()),
default_init: Some(expr),
});
}
}
@ -220,73 +224,7 @@ impl ToIndentedString for ObjectLiteral {
PropertyDefinition::SpreadObject(key) => {
format!("{indentation}...{},\n", key.to_interned_string(interner))
}
PropertyDefinition::MethodDefinition(key, method) => {
format!(
"{indentation}{}({}) {},\n",
match &method {
MethodDefinition::Get(_) =>
format!("get {}", key.to_interned_string(interner)),
MethodDefinition::Set(_) =>
format!("set {}", key.to_interned_string(interner)),
MethodDefinition::Ordinary(_) =>
key.to_interned_string(interner).to_string(),
MethodDefinition::Generator(_) =>
format!("*{}", key.to_interned_string(interner)),
MethodDefinition::AsyncGenerator(_) =>
format!("async *{}", key.to_interned_string(interner)),
MethodDefinition::Async(_) =>
format!("async {}", key.to_interned_string(interner)),
},
match &method {
MethodDefinition::Get(expression)
| MethodDefinition::Set(expression)
| MethodDefinition::Ordinary(expression) => {
join_nodes(interner, expression.parameters().as_ref())
}
MethodDefinition::Generator(expression) => {
join_nodes(interner, expression.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expression) => {
join_nodes(interner, expression.parameters().as_ref())
}
MethodDefinition::Async(expression) => {
join_nodes(interner, expression.parameters().as_ref())
}
},
match &method {
MethodDefinition::Get(expression)
| MethodDefinition::Set(expression)
| MethodDefinition::Ordinary(expression) => {
block_to_string(
expression.body().statements(),
interner,
indent_n + 1,
)
}
MethodDefinition::Generator(expression) => {
block_to_string(
expression.body().statements(),
interner,
indent_n + 1,
)
}
MethodDefinition::AsyncGenerator(expression) => {
block_to_string(
expression.body().statements(),
interner,
indent_n + 1,
)
}
MethodDefinition::Async(expression) => {
block_to_string(
expression.body().statements(),
interner,
indent_n + 1,
)
}
},
)
}
PropertyDefinition::MethodDefinition(m) => m.to_indented_string(interner, indent_n),
PropertyDefinition::CoverInitializedName(ident, expr) => {
format!(
"{indentation}{} = {},\n",
@ -341,3 +279,215 @@ impl VisitWith for ObjectLiteral {
ControlFlow::Continue(())
}
}
/// Describes the definition of a property within an object literal.
///
/// A property has a name (a string) and a value (primitive, method, or object reference).
/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference".
/// This distinction matters because the original referenced object remains unchanged when you change the property's value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyDefinition {
/// Puts a variable into an object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
IdentifierReference(Identifier),
/// Binds a property name to a JavaScript value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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, Expression),
/// A property of an object can also refer to a function or a getter or setter method.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions
MethodDefinition(ObjectMethodDefinition),
/// 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.
///
/// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties
SpreadObject(Expression),
/// Cover grammar for when an object literal is used as an object binding pattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName
CoverInitializedName(Identifier, Expression),
}
impl VisitWith for PropertyDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::IdentifierReference(id) => visitor.visit_identifier(id),
Self::Property(pn, expr) => {
try_break!(visitor.visit_property_name(pn));
visitor.visit_expression(expr)
}
Self::MethodDefinition(m) => visitor.visit_object_method_definition(m),
Self::SpreadObject(expr) => visitor.visit_expression(expr),
Self::CoverInitializedName(id, expr) => {
try_break!(visitor.visit_identifier(id));
visitor.visit_expression(expr)
}
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::IdentifierReference(id) => visitor.visit_identifier_mut(id),
Self::Property(pn, expr) => {
try_break!(visitor.visit_property_name_mut(pn));
visitor.visit_expression_mut(expr)
}
Self::MethodDefinition(m) => visitor.visit_object_method_definition_mut(m),
Self::SpreadObject(expr) => visitor.visit_expression_mut(expr),
Self::CoverInitializedName(id, expr) => {
try_break!(visitor.visit_identifier_mut(id));
visitor.visit_expression_mut(expr)
}
}
}
}
/// A method definition.
///
/// This type is specific to object method definitions.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectMethodDefinition {
name: PropertyName,
parameters: FormalParameterList,
body: FunctionBody,
kind: MethodDefinitionKind,
}
impl ObjectMethodDefinition {
/// Creates a new object method definition.
#[inline]
#[must_use]
pub const fn new(
name: PropertyName,
parameters: FormalParameterList,
body: FunctionBody,
kind: MethodDefinitionKind,
) -> Self {
Self {
name,
parameters,
body,
kind,
}
}
/// Returns the name of the object method definition.
#[inline]
#[must_use]
pub const fn name(&self) -> &PropertyName {
&self.name
}
/// Returns the parameters of the object method definition.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Returns the body of the object method definition.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns the kind of the object method definition.
#[inline]
#[must_use]
pub const fn kind(&self) -> MethodDefinitionKind {
self.kind
}
}
impl ToIndentedString for ObjectMethodDefinition {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let indentation = " ".repeat(indent_n + 1);
let prefix = match &self.kind {
MethodDefinitionKind::Get => "get ",
MethodDefinitionKind::Set => "set ",
MethodDefinitionKind::Ordinary => "",
MethodDefinitionKind::Generator => "*",
MethodDefinitionKind::AsyncGenerator => "async *",
MethodDefinitionKind::Async => "async ",
};
let name = self.name.to_interned_string(interner);
let parameters = join_nodes(interner, self.parameters.as_ref());
let body = block_to_string(self.body.statements(), interner, indent_n + 1);
format!("{indentation}{prefix}{name}({parameters}) {body},\n")
}
}
impl VisitWith for ObjectMethodDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_property_name(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_property_name_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}

108
core/ast/src/expression/mod.rs

@ -15,7 +15,10 @@ use self::{
operator::{Assign, Binary, BinaryInPrivate, Conditional, Unary, Update},
};
use super::{
function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator},
function::{
ArrowFunction, AsyncFunctionExpression, AsyncGeneratorExpression, ClassExpression,
FunctionExpression, GeneratorExpression,
},
function::{AsyncArrowFunction, FormalParameterList},
Statement,
};
@ -88,8 +91,8 @@ pub enum Expression {
/// See [`Spread`],
Spread(Spread),
/// See [`Function`].
Function(Function),
/// See [`FunctionExpression`].
FunctionExpression(FunctionExpression),
/// See [`ArrowFunction`].
ArrowFunction(ArrowFunction),
@ -97,20 +100,18 @@ pub enum Expression {
/// See [`AsyncArrowFunction`].
AsyncArrowFunction(AsyncArrowFunction),
/// See [`Generator`].
Generator(Generator),
/// See [`GeneratorExpression`].
GeneratorExpression(GeneratorExpression),
/// See [`AsyncFunction`].
AsyncFunction(AsyncFunction),
/// See [`AsyncFunctionExpression`].
AsyncFunctionExpression(AsyncFunctionExpression),
/// See [`AsyncGenerator`].
AsyncGenerator(AsyncGenerator),
/// See [`AsyncGeneratorExpression`].
AsyncGeneratorExpression(AsyncGeneratorExpression),
/// See [`Class`].
Class(Box<Class>),
/// See [`ClassExpression`].
ClassExpression(Box<ClassExpression>),
// TODO: Extract regexp literal Expression
// RegExpLiteral,
/// See [`TemplateLiteral`].
TemplateLiteral(TemplateLiteral),
@ -189,13 +190,15 @@ impl Expression {
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::FunctionExpression(f) => f.to_indented_string(interner, indentation),
Self::AsyncArrowFunction(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::ClassExpression(cl) => cl.to_indented_string(interner, indentation),
Self::GeneratorExpression(gen) => gen.to_indented_string(interner, indentation),
Self::AsyncFunctionExpression(asf) => asf.to_indented_string(interner, indentation),
Self::AsyncGeneratorExpression(asgen) => {
asgen.to_indented_string(interner, indentation)
}
Self::TemplateLiteral(tem) => tem.to_interned_string(interner),
Self::PropertyAccess(prop) => prop.to_interned_string(interner),
Self::New(new) => new.to_interned_string(interner),
@ -220,27 +223,6 @@ impl Expression {
}
}
/// Returns if the expression is a function definition according to the spec.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-isfunctiondefinition
#[must_use]
#[inline]
pub const fn is_function_definition(&self) -> bool {
matches!(
self,
Self::ArrowFunction(_)
| Self::AsyncArrowFunction(_)
| Self::Function(_)
| Self::Generator(_)
| Self::AsyncGenerator(_)
| Self::AsyncFunction(_)
| Self::Class(_)
)
}
/// Returns if the expression is a function definition without a name.
///
/// More information:
@ -253,16 +235,34 @@ impl Expression {
match self {
Self::ArrowFunction(f) => f.name().is_none(),
Self::AsyncArrowFunction(f) => f.name().is_none(),
Self::Function(f) => f.name().is_none(),
Self::Generator(f) => f.name().is_none(),
Self::AsyncGenerator(f) => f.name().is_none(),
Self::AsyncFunction(f) => f.name().is_none(),
Self::Class(f) => f.name().is_none(),
Self::FunctionExpression(f) => f.name().is_none(),
Self::GeneratorExpression(f) => f.name().is_none(),
Self::AsyncGeneratorExpression(f) => f.name().is_none(),
Self::AsyncFunctionExpression(f) => f.name().is_none(),
Self::ClassExpression(f) => f.name().is_none(),
Self::Parenthesized(p) => p.expression().is_anonymous_function_definition(),
_ => false,
}
}
/// Sets the name of an anonymous function definition.
///
/// This is used to set the name of a function expression when it is assigned to a variable.
/// If the function already has a name, this does nothing.
pub fn set_anonymous_function_definition_name(&mut self, name: &Identifier) {
match self {
Self::ArrowFunction(f) if f.name().is_none() => f.name = Some(*name),
Self::AsyncArrowFunction(f) if f.name().is_none() => f.name = Some(*name),
Self::FunctionExpression(f) if f.name().is_none() => f.name = Some(*name),
Self::GeneratorExpression(f) if f.name().is_none() => f.name = Some(*name),
Self::AsyncGeneratorExpression(f) if f.name().is_none() => f.name = Some(*name),
Self::AsyncFunctionExpression(f) if f.name().is_none() => f.name = Some(*name),
Self::ClassExpression(f) if f.name().is_none() => f.name = Some(*name),
Self::Parenthesized(p) => p.expression.set_anonymous_function_definition_name(name),
_ => {}
}
}
/// Returns the expression without any outer parenthesized expressions.
#[must_use]
#[inline]
@ -301,13 +301,13 @@ impl VisitWith for Expression {
Self::ArrayLiteral(arlit) => visitor.visit_array_literal(arlit),
Self::ObjectLiteral(olit) => visitor.visit_object_literal(olit),
Self::Spread(sp) => visitor.visit_spread(sp),
Self::Function(f) => visitor.visit_function(f),
Self::FunctionExpression(f) => visitor.visit_function_expression(f),
Self::ArrowFunction(af) => visitor.visit_arrow_function(af),
Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af),
Self::Generator(g) => visitor.visit_generator(g),
Self::AsyncFunction(af) => visitor.visit_async_function(af),
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
Self::Class(c) => visitor.visit_class(c),
Self::GeneratorExpression(g) => visitor.visit_generator_expression(g),
Self::AsyncFunctionExpression(af) => visitor.visit_async_function_expression(af),
Self::AsyncGeneratorExpression(ag) => visitor.visit_async_generator_expression(ag),
Self::ClassExpression(c) => visitor.visit_class_expression(c),
Self::TemplateLiteral(tlit) => visitor.visit_template_literal(tlit),
Self::PropertyAccess(pa) => visitor.visit_property_access(pa),
Self::New(n) => visitor.visit_new(n),
@ -344,13 +344,13 @@ impl VisitWith for Expression {
Self::ArrayLiteral(arlit) => visitor.visit_array_literal_mut(arlit),
Self::ObjectLiteral(olit) => visitor.visit_object_literal_mut(olit),
Self::Spread(sp) => visitor.visit_spread_mut(sp),
Self::Function(f) => visitor.visit_function_mut(f),
Self::FunctionExpression(f) => visitor.visit_function_expression_mut(f),
Self::ArrowFunction(af) => visitor.visit_arrow_function_mut(af),
Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af),
Self::Generator(g) => visitor.visit_generator_mut(g),
Self::AsyncFunction(af) => visitor.visit_async_function_mut(af),
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Self::Class(c) => visitor.visit_class_mut(c),
Self::GeneratorExpression(g) => visitor.visit_generator_expression_mut(g),
Self::AsyncFunctionExpression(af) => visitor.visit_async_function_expression_mut(af),
Self::AsyncGeneratorExpression(ag) => visitor.visit_async_generator_expression_mut(ag),
Self::ClassExpression(c) => visitor.visit_class_expression_mut(c),
Self::TemplateLiteral(tlit) => visitor.visit_template_literal_mut(tlit),
Self::PropertyAccess(pa) => visitor.visit_property_access_mut(pa),
Self::New(n) => visitor.visit_new_mut(n),

2
core/ast/src/expression/parenthesized.rs

@ -15,7 +15,7 @@ use core::ops::ControlFlow;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Parenthesized {
expression: Box<Expression>,
pub(crate) expression: Box<Expression>,
}
impl Parenthesized {

2
core/ast/src/function/arrow_function.rs

@ -22,7 +22,7 @@ use super::{FormalParameterList, FunctionBody};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrowFunction {
name: Option<Identifier>,
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
}

2
core/ast/src/function/async_arrow_function.rs

@ -22,7 +22,7 @@ use boa_interner::{Interner, ToIndentedString};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncArrowFunction {
name: Option<Identifier>,
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
}

144
core/ast/src/function/async_function.rs

@ -1,36 +1,129 @@
//! Async Function Expression.
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use super::{FormalParameterList, FunctionBody};
use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes, Declaration,
join_nodes, try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Declaration,
};
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
use super::{FormalParameterList, FunctionBody};
/// An async function declaration.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncFunctionDeclaration {
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
}
impl AsyncFunctionDeclaration {
/// Creates a new async function declaration.
#[inline]
#[must_use]
pub const fn new(
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
Self {
name,
parameters,
body,
}
}
/// Gets the name of the async function declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Identifier {
self.name
}
/// An async function definition, as defined by the [spec].
/// Gets the list of parameters of the async function declaration.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the async function declaration.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
}
impl ToIndentedString for AsyncFunctionDeclaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"async function {}({}) {}",
interner.resolve_expect(self.name.sym()),
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
)
}
}
impl VisitWith for AsyncFunctionDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_identifier(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_identifier_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}
impl From<AsyncFunctionDeclaration> for Declaration {
#[inline]
fn from(f: AsyncFunctionDeclaration) -> Self {
Self::AsyncFunctionDeclaration(f)
}
}
/// An async function expression.
///
/// An [async function][mdn] is a function where await expressions are allowed within it.
/// The async and await keywords enable asynchronous programming on Javascript without the use
/// of promise chains.
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions
/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncFunction {
name: Option<Identifier>,
pub struct AsyncFunctionExpression {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
}
impl AsyncFunction {
/// Creates a new function expression
impl AsyncFunctionExpression {
/// Creates a new async function expression.
#[inline]
#[must_use]
pub const fn new(
@ -47,28 +140,28 @@ impl AsyncFunction {
}
}
/// Gets the name of the function declaration.
/// Gets the name of the async function expression.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Gets the list of parameters of the function declaration.
/// Gets the list of parameters of the async function expression.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function declaration.
/// Gets the body of the async function expression.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns whether the function expression has a binding identifier.
/// Returns whether the async function expression has a binding identifier.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
@ -76,7 +169,7 @@ impl AsyncFunction {
}
}
impl ToIndentedString for AsyncFunction {
impl ToIndentedString for AsyncFunctionExpression {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "async function".to_owned();
if self.has_binding_identifier {
@ -101,21 +194,14 @@ impl ToIndentedString for AsyncFunction {
}
}
impl From<AsyncFunction> for Expression {
#[inline]
fn from(expr: AsyncFunction) -> Self {
Self::AsyncFunction(expr)
}
}
impl From<AsyncFunction> for Declaration {
impl From<AsyncFunctionExpression> for Expression {
#[inline]
fn from(f: AsyncFunction) -> Self {
Self::AsyncFunction(f)
fn from(expr: AsyncFunctionExpression) -> Self {
Self::AsyncFunctionExpression(expr)
}
}
impl VisitWith for AsyncFunction {
impl VisitWith for AsyncFunctionExpression {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,

135
core/ast/src/function/async_generator.rs

@ -11,25 +11,119 @@ use core::ops::ControlFlow;
use super::{FormalParameterList, FunctionBody};
/// An async generator definition, as defined by the [spec].
/// An async generator declaration.
///
/// An [async generator][mdn] combines async functions with generators, making it possible to use
/// `await` and `yield` expressions within the definition of the function.
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncGenerator {
name: Option<Identifier>,
pub struct AsyncGeneratorDeclaration {
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
}
impl AsyncGeneratorDeclaration {
/// Creates a new async generator declaration.
#[inline]
#[must_use]
pub const fn new(
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
Self {
name,
parameters,
body,
}
}
/// Gets the name of the async generator declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Identifier {
self.name
}
/// Gets the list of parameters of the async generator declaration.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the async generator declaration.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
}
impl ToIndentedString for AsyncGeneratorDeclaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"async function* {}({}) {}",
interner.resolve_expect(self.name.sym()),
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
)
}
}
impl VisitWith for AsyncGeneratorDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_identifier(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_identifier_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}
impl From<AsyncGeneratorDeclaration> for Declaration {
#[inline]
fn from(f: AsyncGeneratorDeclaration) -> Self {
Self::AsyncGeneratorDeclaration(f)
}
}
/// An async generator expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncGeneratorExpression {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
}
impl AsyncGenerator {
/// Creates a new async generator expression
impl AsyncGeneratorExpression {
/// Creates a new async generator expression.
#[inline]
#[must_use]
pub const fn new(
@ -46,28 +140,28 @@ impl AsyncGenerator {
}
}
/// Gets the name of the async generator expression
/// Gets the name of the async generator expression.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Gets the list of parameters of the async generator expression
/// Gets the list of parameters of the async generator expression.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the async generator expression
/// Gets the body of the async generator expression.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns whether the function expression has a binding identifier.
/// Returns whether the async generator expression has a binding identifier.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
@ -75,7 +169,7 @@ impl AsyncGenerator {
}
}
impl ToIndentedString for AsyncGenerator {
impl ToIndentedString for AsyncGeneratorExpression {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "async function*".to_owned();
if self.has_binding_identifier {
@ -93,21 +187,14 @@ impl ToIndentedString for AsyncGenerator {
}
}
impl From<AsyncGenerator> for Expression {
#[inline]
fn from(expr: AsyncGenerator) -> Self {
Self::AsyncGenerator(expr)
}
}
impl From<AsyncGenerator> for Declaration {
impl From<AsyncGeneratorExpression> for Expression {
#[inline]
fn from(f: AsyncGenerator) -> Self {
Self::AsyncGenerator(f)
fn from(expr: AsyncGeneratorExpression) -> Self {
Self::AsyncGeneratorExpression(expr)
}
}
impl VisitWith for AsyncGenerator {
impl VisitWith for AsyncGeneratorExpression {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,

712
core/ast/src/function/class.rs

@ -1,9 +1,9 @@
use super::Function;
use super::{FormalParameterList, FunctionBody, FunctionExpression};
use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes,
property::{MethodDefinition, PropertyName},
property::{MethodDefinitionKind, PropertyName},
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Declaration,
@ -12,81 +12,212 @@ use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
use std::hash::Hash;
/// A class declaration, as defined by the [spec].
/// A class declaration.
///
/// A [class][mdn] 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/Classes
/// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Class {
name: Option<Identifier>,
pub struct ClassDeclaration {
name: Identifier,
super_ref: Option<Expression>,
pub(crate) constructor: Option<Function>,
pub(crate) constructor: Option<FunctionExpression>,
pub(crate) elements: Box<[ClassElement]>,
has_binding_identifier: bool,
}
impl Class {
impl ClassDeclaration {
/// Creates a new class declaration.
#[inline]
#[must_use]
pub fn new(
name: Option<Identifier>,
name: Identifier,
super_ref: Option<Expression>,
constructor: Option<Function>,
constructor: Option<FunctionExpression>,
elements: Box<[ClassElement]>,
has_binding_identifier: bool,
) -> Self {
Self {
name,
super_ref,
constructor,
elements,
has_binding_identifier,
}
}
/// Returns the name of the class.
/// Returns the name of the class declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
pub const fn name(&self) -> Identifier {
self.name
}
/// Returns the super class ref of the class.
/// Returns the super class ref of the class declaration.
#[inline]
#[must_use]
pub const fn super_ref(&self) -> Option<&Expression> {
self.super_ref.as_ref()
}
/// Returns the constructor of the class.
/// Returns the constructor of the class declaration.
#[inline]
#[must_use]
pub const fn constructor(&self) -> Option<&Function> {
pub const fn constructor(&self) -> Option<&FunctionExpression> {
self.constructor.as_ref()
}
/// Gets the list of all fields defined on the class.
/// Gets the list of all fields defined on the class declaration.
#[inline]
#[must_use]
pub const fn elements(&self) -> &[ClassElement] {
&self.elements
}
}
/// Returns whether the class has a binding identifier.
impl ToIndentedString for ClassDeclaration {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let mut buf = format!("class {}", interner.resolve_expect(self.name.sym()));
if let Some(super_ref) = self.super_ref.as_ref() {
buf.push_str(&format!(
" extends {}",
super_ref.to_interned_string(interner)
));
}
if self.elements.is_empty() && self.constructor().is_none() {
buf.push_str(" {}");
return buf;
}
let indentation = " ".repeat(indent_n + 1);
buf.push_str(" {\n");
if let Some(expr) = &self.constructor {
buf.push_str(&format!(
"{indentation}constructor({}) {}\n",
join_nodes(interner, expr.parameters().as_ref()),
block_to_string(expr.body().statements(), interner, indent_n + 1)
));
}
for element in &self.elements {
buf.push_str(&element.to_indented_string(interner, indent_n));
}
buf.push('}');
buf
}
}
impl VisitWith for ClassDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_identifier(&self.name));
if let Some(expr) = &self.super_ref {
try_break!(visitor.visit_expression(expr));
}
if let Some(func) = &self.constructor {
try_break!(visitor.visit_function_expression(func));
}
for elem in &*self.elements {
try_break!(visitor.visit_class_element(elem));
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_identifier_mut(&mut self.name));
if let Some(expr) = &mut self.super_ref {
try_break!(visitor.visit_expression_mut(expr));
}
if let Some(func) = &mut self.constructor {
try_break!(visitor.visit_function_expression_mut(func));
}
for elem in &mut *self.elements {
try_break!(visitor.visit_class_element_mut(elem));
}
ControlFlow::Continue(())
}
}
impl From<ClassDeclaration> for Declaration {
fn from(f: ClassDeclaration) -> Self {
Self::ClassDeclaration(f)
}
}
/// A class expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ClassExpression {
pub(crate) name: Option<Identifier>,
super_ref: Option<Expression>,
pub(crate) constructor: Option<FunctionExpression>,
pub(crate) elements: Box<[ClassElement]>,
has_binding_identifier: bool,
}
impl ClassExpression {
/// Creates a new class expression.
#[inline]
#[must_use]
pub fn new(
name: Option<Identifier>,
super_ref: Option<Expression>,
constructor: Option<FunctionExpression>,
elements: Box<[ClassElement]>,
has_binding_identifier: bool,
) -> Self {
Self {
name,
super_ref,
constructor,
elements,
has_binding_identifier,
}
}
/// Returns the name of the class expression.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Returns the super class ref of the class expression.
#[inline]
#[must_use]
pub const fn super_ref(&self) -> Option<&Expression> {
self.super_ref.as_ref()
}
/// Returns the constructor of the class expression.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
pub const fn constructor(&self) -> Option<&FunctionExpression> {
self.constructor.as_ref()
}
/// Gets the list of all fields defined on the class expression.
#[inline]
#[must_use]
pub const fn elements(&self) -> &[ClassElement] {
&self.elements
}
}
impl ToIndentedString for Class {
impl ToIndentedString for ClassExpression {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let mut buf = "class".to_string();
if self.has_binding_identifier {
@ -113,263 +244,21 @@ impl ToIndentedString for Class {
block_to_string(expr.body().statements(), interner, indent_n + 1)
));
}
for element in &*self.elements {
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().as_ref())
}
MethodDefinition::Generator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Generator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::AsyncGenerator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Async(expr) => {
block_to_string(expr.body().statements(), 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().as_ref())
}
MethodDefinition::Generator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Generator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::AsyncGenerator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Async(expr) => {
block_to_string(expr.body().statements(), 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.description()),
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Generator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::AsyncGenerator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Async(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
},
)
}
ClassElement::PrivateStaticMethodDefinition(name, method) => {
format!(
"{indentation}static {}#{}({}) {}\n",
match &method {
MethodDefinition::Get(_) => "get ",
MethodDefinition::Set(_) => "set ",
_ => "",
},
interner.resolve_expect(name.description()),
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Generator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::AsyncGenerator(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
MethodDefinition::Async(expr) => {
block_to_string(expr.body().statements(), interner, indent_n + 1)
}
},
)
}
ClassElement::PrivateFieldDefinition(name, field) => match field {
Some(expr) => {
format!(
"{indentation}#{} = {};\n",
interner.resolve_expect(name.description()),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
None => {
format!(
"{indentation}#{};\n",
interner.resolve_expect(name.description()),
)
}
},
ClassElement::PrivateStaticFieldDefinition(name, field) => match field {
Some(expr) => {
format!(
"{indentation}static #{} = {};\n",
interner.resolve_expect(name.description()),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
None => {
format!(
"{indentation}static #{};\n",
interner.resolve_expect(name.description()),
)
}
},
ClassElement::StaticBlock(body) => {
format!(
"{indentation}static {}\n",
block_to_string(body.statements(), interner, indent_n + 1)
)
}
});
for element in &self.elements {
buf.push_str(&element.to_indented_string(interner, indent_n));
}
buf.push('}');
buf
}
}
impl From<Class> for Expression {
fn from(expr: Class) -> Self {
Self::Class(Box::new(expr))
}
}
impl From<Class> for Declaration {
fn from(f: Class) -> Self {
Self::Class(f)
impl From<ClassExpression> for Expression {
fn from(expr: ClassExpression) -> Self {
Self::ClassExpression(Box::new(expr))
}
}
impl VisitWith for Class {
impl VisitWith for ClassExpression {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
@ -381,7 +270,7 @@ impl VisitWith for Class {
try_break!(visitor.visit_expression(expr));
}
if let Some(func) = &self.constructor {
try_break!(visitor.visit_function(func));
try_break!(visitor.visit_function_expression(func));
}
for elem in &*self.elements {
try_break!(visitor.visit_class_element(elem));
@ -400,7 +289,7 @@ impl VisitWith for Class {
try_break!(visitor.visit_expression_mut(expr));
}
if let Some(func) = &mut self.constructor {
try_break!(visitor.visit_function_mut(func));
try_break!(visitor.visit_function_expression_mut(func));
}
for elem in &mut *self.elements {
try_break!(visitor.visit_class_element_mut(elem));
@ -416,18 +305,18 @@ impl VisitWith for Class {
/// [spec]: https://tc39.es/ecma262/#prod-ClassStaticBlockBody
type StaticBlockBody = crate::Script;
/// An element that can be within a [`Class`], as defined by the [spec].
/// An element that can be within a class.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ClassElement {
/// A method definition, including `get` and `set` accessors.
MethodDefinition(PropertyName, MethodDefinition),
/// A static method definition, accessible from the class constructor object.
StaticMethodDefinition(PropertyName, MethodDefinition),
/// A method definition.
MethodDefinition(ClassMethodDefinition),
/// A field definition.
FieldDefinition(PropertyName, Option<Expression>),
@ -435,13 +324,6 @@ pub enum ClassElement {
/// A static field definition, accessible from the class constructor object
StaticFieldDefinition(PropertyName, Option<Expression>),
/// A private method definition, only accessible inside the class declaration.
PrivateMethodDefinition(PrivateName, MethodDefinition),
/// A private static method definition, only accessible from static methods and fields inside
/// the class declaration.
PrivateStaticMethodDefinition(PrivateName, MethodDefinition),
/// A private field definition, only accessible inside the class declaration.
PrivateFieldDefinition(PrivateName, Option<Expression>),
@ -453,15 +335,95 @@ pub enum ClassElement {
StaticBlock(StaticBlockBody),
}
impl ToIndentedString for ClassElement {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let indentation = " ".repeat(indent_n + 1);
match self {
Self::MethodDefinition(m) => m.to_indented_string(interner, indent_n),
Self::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),)
}
},
Self::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),
)
}
},
Self::PrivateFieldDefinition(name, field) => match field {
Some(expr) => {
format!(
"{indentation}#{} = {};\n",
interner.resolve_expect(name.description()),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
None => {
format!(
"{indentation}#{};\n",
interner.resolve_expect(name.description()),
)
}
},
Self::PrivateStaticFieldDefinition(name, field) => match field {
Some(expr) => {
format!(
"{indentation}static #{} = {};\n",
interner.resolve_expect(name.description()),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
None => {
format!(
"{indentation}static #{};\n",
interner.resolve_expect(name.description()),
)
}
},
Self::StaticBlock(body) => {
format!(
"{indentation}static {}\n",
block_to_string(body.statements(), interner, indent_n + 1)
)
}
}
}
}
impl VisitWith for ClassElement {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => {
try_break!(visitor.visit_property_name(pn));
visitor.visit_method_definition(md)
Self::MethodDefinition(m) => {
match &m.name {
ClassElementName::PropertyName(pn) => {
try_break!(visitor.visit_property_name(pn));
}
ClassElementName::PrivateName(pn) => {
try_break!(visitor.visit_private_name(pn));
}
}
try_break!(visitor.visit_formal_parameter_list(&m.parameters));
visitor.visit_script(&m.body)
}
Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => {
try_break!(visitor.visit_property_name(pn));
@ -471,11 +433,6 @@ impl VisitWith for ClassElement {
ControlFlow::Continue(())
}
}
Self::PrivateMethodDefinition(name, md)
| Self::PrivateStaticMethodDefinition(name, md) => {
try_break!(visitor.visit_private_name(name));
visitor.visit_method_definition(md)
}
Self::PrivateFieldDefinition(name, maybe_expr)
| Self::PrivateStaticFieldDefinition(name, maybe_expr) => {
try_break!(visitor.visit_private_name(name));
@ -494,9 +451,17 @@ impl VisitWith for ClassElement {
V: VisitorMut<'a>,
{
match self {
Self::MethodDefinition(pn, md) | Self::StaticMethodDefinition(pn, md) => {
try_break!(visitor.visit_property_name_mut(pn));
visitor.visit_method_definition_mut(md)
Self::MethodDefinition(m) => {
match m.name {
ClassElementName::PropertyName(ref mut pn) => {
try_break!(visitor.visit_property_name_mut(pn));
}
ClassElementName::PrivateName(ref mut pn) => {
try_break!(visitor.visit_private_name_mut(pn));
}
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut m.parameters));
visitor.visit_script_mut(&mut m.body)
}
Self::FieldDefinition(pn, maybe_expr) | Self::StaticFieldDefinition(pn, maybe_expr) => {
try_break!(visitor.visit_property_name_mut(pn));
@ -506,11 +471,6 @@ impl VisitWith for ClassElement {
ControlFlow::Continue(())
}
}
Self::PrivateMethodDefinition(name, md)
| Self::PrivateStaticMethodDefinition(name, md) => {
try_break!(visitor.visit_private_name_mut(name));
visitor.visit_method_definition_mut(md)
}
Self::PrivateFieldDefinition(name, maybe_expr)
| Self::PrivateStaticFieldDefinition(name, maybe_expr) => {
try_break!(visitor.visit_private_name_mut(name));
@ -525,6 +485,148 @@ impl VisitWith for ClassElement {
}
}
/// A method definition.
///
/// This type is specific to class method definitions.
/// It includes private names and the information about whether the method is static or not.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ClassMethodDefinition {
name: ClassElementName,
parameters: FormalParameterList,
body: FunctionBody,
kind: MethodDefinitionKind,
is_static: bool,
}
impl ClassMethodDefinition {
/// Creates a new class method definition.
#[inline]
#[must_use]
pub const fn new(
name: ClassElementName,
parameters: FormalParameterList,
body: FunctionBody,
kind: MethodDefinitionKind,
is_static: bool,
) -> Self {
Self {
name,
parameters,
body,
kind,
is_static,
}
}
/// Returns the name of the class method definition.
#[inline]
#[must_use]
pub const fn name(&self) -> &ClassElementName {
&self.name
}
/// Returns the parameters of the class method definition.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Returns the body of the class method definition.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns the kind of the class method definition.
#[inline]
#[must_use]
pub const fn kind(&self) -> MethodDefinitionKind {
self.kind
}
/// Returns whether the class method definition is static.
#[inline]
#[must_use]
pub const fn is_static(&self) -> bool {
self.is_static
}
/// Returns whether the class method definition is private.
#[inline]
#[must_use]
pub const fn is_private(&self) -> bool {
self.name.is_private()
}
}
impl ToIndentedString for ClassMethodDefinition {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let indentation = " ".repeat(indent_n + 1);
let prefix = match (self.is_static, &self.kind) {
(true, MethodDefinitionKind::Get) => "static get ",
(true, MethodDefinitionKind::Set) => "static set ",
(true, MethodDefinitionKind::Ordinary) => "static ",
(true, MethodDefinitionKind::Generator) => "static *",
(true, MethodDefinitionKind::AsyncGenerator) => "static async *",
(true, MethodDefinitionKind::Async) => "static async ",
(false, MethodDefinitionKind::Get) => "get ",
(false, MethodDefinitionKind::Set) => "set ",
(false, MethodDefinitionKind::Ordinary) => "",
(false, MethodDefinitionKind::Generator) => "*",
(false, MethodDefinitionKind::AsyncGenerator) => "async *",
(false, MethodDefinitionKind::Async) => "async ",
};
let name = self.name.to_interned_string(interner);
let parameters = join_nodes(interner, self.parameters.as_ref());
let body = block_to_string(self.body.statements(), interner, indent_n + 1);
format!("{indentation}{prefix}{name}({parameters}) {body}\n")
}
}
/// A class element name.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ClassElementName {
/// A property name.
PropertyName(PropertyName),
/// A private name.
PrivateName(PrivateName),
}
impl ClassElementName {
/// Returns whether the class element name is private.
#[inline]
#[must_use]
pub const fn is_private(&self) -> bool {
matches!(self, Self::PrivateName(_))
}
}
impl ToInternedString for ClassElementName {
fn to_interned_string(&self, interner: &Interner) -> String {
match &self {
Self::PropertyName(name) => name.to_interned_string(interner),
Self::PrivateName(name) => format!("#{}", interner.resolve_expect(name.description())),
}
}
}
/// A private name as defined by the [spec].
///
/// [spec]: https://tc39.es/ecma262/#sec-private-names

145
core/ast/src/function/generator.rs

@ -1,37 +1,127 @@
use super::{FormalParameterList, FunctionBody};
use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes, Declaration,
join_nodes, try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Declaration,
};
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, ToIndentedString};
/// A generator declaration.
///
/// 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 = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct GeneratorDeclaration {
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
}
use super::{FormalParameterList, FunctionBody};
impl GeneratorDeclaration {
/// Creates a new generator declaration.
#[inline]
#[must_use]
pub const fn new(
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
Self {
name,
parameters,
body,
}
}
/// Gets the name of the generator declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Identifier {
self.name
}
/// Gets the list of parameters of the generator declaration.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the generator declaration.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
}
impl ToIndentedString for GeneratorDeclaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"function* {}({}) {}",
interner.resolve_expect(self.name.sym()),
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
)
}
}
impl VisitWith for GeneratorDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_identifier(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
/// A generator definition, as defined by the [spec].
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_identifier_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}
impl From<GeneratorDeclaration> for Declaration {
#[inline]
fn from(f: GeneratorDeclaration) -> Self {
Self::GeneratorDeclaration(f)
}
}
/// A generator expression.
///
/// [Generators][mdn] are "resumable functions", which can be suspended during execution and
/// resumed at any later time. The main feature of a generator are `yield` expressions, which
/// specifies the value returned when a generator is suspended, and the point from which
/// the execution will resume.
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions
/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Generator {
name: Option<Identifier>,
pub struct GeneratorExpression {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
}
impl Generator {
/// Creates a new generator expression
impl GeneratorExpression {
/// Creates a new generator expression.
#[inline]
#[must_use]
pub const fn new(
@ -48,28 +138,28 @@ impl Generator {
}
}
/// Gets the name of the generator declaration.
/// Gets the name of the generator expression.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Gets the list of parameters of the generator declaration.
/// Gets the list of parameters of the generator expression.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the generator declaration.
/// Gets the body of the generator expression.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns whether the function expression has a binding identifier.
/// Returns whether the generator expression has a binding identifier.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
@ -77,7 +167,7 @@ impl Generator {
}
}
impl ToIndentedString for Generator {
impl ToIndentedString for GeneratorExpression {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "function*".to_owned();
if self.has_binding_identifier {
@ -95,21 +185,14 @@ impl ToIndentedString for Generator {
}
}
impl From<Generator> for Expression {
#[inline]
fn from(expr: Generator) -> Self {
Self::Generator(expr)
}
}
impl From<Generator> for Declaration {
impl From<GeneratorExpression> for Expression {
#[inline]
fn from(f: Generator) -> Self {
Self::Generator(f)
fn from(expr: GeneratorExpression) -> Self {
Self::GeneratorExpression(expr)
}
}
impl VisitWith for Generator {
impl VisitWith for GeneratorExpression {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,

203
core/ast/src/function/mod.rs

@ -1,25 +1,27 @@
//! Functions and classes nodes, as defined by the [spec].
//! This module contains Function and Class AST nodes.
//!
//! [Functions][func] are mainly subprograms that can be called by external code to execute a sequence of
//! statements (the *body* of the function). Javascript functions fall in several categories:
//! ECMAScript defines multiple types of functions and classes.
//! They are split into different AST nodes to reduce ambiguity and to make the AST more readable.
//!
//! - [`Function`]s.
//! - [`ArrowFunction`]s.
//! - [`AsyncArrowFunction`]s.
//! - [`Generator`]s.
//! - [`AsyncFunction`]s.
//! - [`AsyncGenerator`]s.
//!
//! All of them can be declared in either [declaration][decl] form or [expression][expr] form,
//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form.
//!
//! This module also contains [`Class`]es, which are templates for creating objects. Classes
//! can also be declared in either declaration or expression form.
//!
//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-functions-and-classes
//! [func]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
//! [decl]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
//! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
//! - Functions:
//! - [`FunctionDeclaration`]
//! - [`FunctionExpression`]
//! - Async functions:
//! - [`AsyncFunctionDeclaration`]
//! - [`AsyncFunctionExpression`]
//! - Generators
//! - [`GeneratorDeclaration`]
//! - [`GeneratorExpression`]
//! - Async Generators
//! - [`AsyncGeneratorDeclaration`]
//! - [`AsyncGeneratorExpression`]
//! - Arrow Functions
//! - [`ArrowFunction`]
//! - Async Arrow Functions
//! - [`AsyncArrowFunction`]
//! - Classes
//! - [`ClassDeclaration`]
//! - [`ClassExpression`]
mod arrow_function;
mod async_arrow_function;
@ -27,163 +29,22 @@ mod async_function;
mod async_generator;
mod class;
mod generator;
mod ordinary_function;
mod parameters;
pub use arrow_function::ArrowFunction;
pub use async_arrow_function::AsyncArrowFunction;
pub use async_function::AsyncFunction;
pub use async_generator::AsyncGenerator;
pub use class::{Class, ClassElement, PrivateName};
use core::ops::ControlFlow;
pub use generator::Generator;
pub use async_function::{AsyncFunctionDeclaration, AsyncFunctionExpression};
pub use async_generator::{AsyncGeneratorDeclaration, AsyncGeneratorExpression};
pub use class::{
ClassDeclaration, ClassElement, ClassElementName, ClassExpression, ClassMethodDefinition,
PrivateName,
};
pub use generator::{GeneratorDeclaration, GeneratorExpression};
pub use ordinary_function::{FunctionDeclaration, FunctionExpression};
pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags};
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{block_to_string, join_nodes};
use crate::{try_break, Script};
use boa_interner::{Interner, ToIndentedString};
use super::expression::{Expression, Identifier};
use super::Declaration;
/// A function definition, as defined by the [spec].
///
/// 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:
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
}
impl Function {
/// Creates a new function expression.
#[inline]
#[must_use]
pub const fn new(
name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier: false,
}
}
/// Creates a new function expression with an expression binding identifier.
#[inline]
#[must_use]
pub const fn new_with_binding_identifier(
name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}
/// Gets the name of the function declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Gets the list of parameters of the function declaration.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function declaration.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}
impl ToIndentedString for Function {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "function".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
}
}
buf.push_str(&format!(
"({}) {}",
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
));
buf
}
}
impl From<Function> for Expression {
#[inline]
fn from(expr: Function) -> Self {
Self::Function(expr)
}
}
impl From<Function> for Declaration {
#[inline]
fn from(f: Function) -> Self {
Self::Function(f)
}
}
impl VisitWith for Function {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(ident) = &self.name {
try_break!(visitor.visit_identifier(ident));
}
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(ident) = &mut self.name {
try_break!(visitor.visit_identifier_mut(ident));
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}
use crate::Script;
/// A Function body.
///

217
core/ast/src/function/ordinary_function.rs

@ -0,0 +1,217 @@
use super::{FormalParameterList, FunctionBody};
use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes, try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Declaration,
};
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
/// A function declaration.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionDeclaration {
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
}
impl FunctionDeclaration {
/// Creates a new function declaration.
#[inline]
#[must_use]
pub const fn new(
name: Identifier,
parameters: FormalParameterList,
body: FunctionBody,
) -> Self {
Self {
name,
parameters,
body,
}
}
/// Gets the name of the function declaration.
#[inline]
#[must_use]
pub const fn name(&self) -> Identifier {
self.name
}
/// Gets the list of parameters of the function declaration.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function declaration.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
}
impl ToIndentedString for FunctionDeclaration {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"function {}({}) {}",
interner.resolve_expect(self.name.sym()),
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
)
}
}
impl VisitWith for FunctionDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_identifier(&self.name));
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_identifier_mut(&mut self.name));
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}
impl From<FunctionDeclaration> for Declaration {
#[inline]
fn from(f: FunctionDeclaration) -> Self {
Self::FunctionDeclaration(f)
}
}
/// A function expression.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionExpression {
pub(crate) name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
}
impl FunctionExpression {
/// Creates a new function expression.
#[inline]
#[must_use]
pub const fn new(
name: Option<Identifier>,
parameters: FormalParameterList,
body: FunctionBody,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}
/// Gets the name of the function expression.
#[inline]
#[must_use]
pub const fn name(&self) -> Option<Identifier> {
self.name
}
/// Gets the list of parameters of the function expression.
#[inline]
#[must_use]
pub const fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function expression.
#[inline]
#[must_use]
pub const fn body(&self) -> &FunctionBody {
&self.body
}
/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub const fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}
impl ToIndentedString for FunctionExpression {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "function".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
}
}
buf.push_str(&format!(
"({}) {}",
join_nodes(interner, self.parameters.as_ref()),
block_to_string(self.body.statements(), interner, indentation)
));
buf
}
}
impl From<FunctionExpression> for Expression {
#[inline]
fn from(expr: FunctionExpression) -> Self {
Self::FunctionExpression(expr)
}
}
impl VisitWith for FunctionExpression {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(ident) = &self.name {
try_break!(visitor.visit_identifier(ident));
}
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_script(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(ident) = &mut self.name {
try_break!(visitor.visit_identifier_mut(ident));
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_script_mut(&mut self.body)
}
}

48
core/ast/src/module_item_list/mod.rs

@ -5,12 +5,6 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-modules
use std::{convert::Infallible, hash::BuildHasherDefault, ops::ControlFlow};
use boa_interner::Sym;
use indexmap::IndexSet;
use rustc_hash::{FxHashSet, FxHasher};
use crate::{
declaration::{
ExportDeclaration, ExportEntry, ExportSpecifier, ImportDeclaration, ImportEntry,
@ -23,6 +17,10 @@ use crate::{
visitor::{VisitWith, Visitor, VisitorMut},
StatementListItem,
};
use boa_interner::Sym;
use indexmap::IndexSet;
use rustc_hash::{FxHashSet, FxHasher};
use std::{convert::Infallible, hash::BuildHasherDefault, ops::ControlFlow};
/// Module item list AST node.
///
@ -106,10 +104,10 @@ impl ModuleItemList {
ExportDeclaration::Declaration(decl) => {
BoundNamesVisitor(self.0).visit_declaration(decl)
}
ExportDeclaration::DefaultFunction(_)
| ExportDeclaration::DefaultGenerator(_)
| ExportDeclaration::DefaultAsyncFunction(_)
| ExportDeclaration::DefaultAsyncGenerator(_)
ExportDeclaration::DefaultFunctionDeclaration(_)
| ExportDeclaration::DefaultGeneratorDeclaration(_)
| ExportDeclaration::DefaultAsyncFunctionDeclaration(_)
| ExportDeclaration::DefaultAsyncGeneratorDeclaration(_)
| ExportDeclaration::DefaultClassDeclaration(_)
| ExportDeclaration::DefaultAssignmentExpression(_) => {
self.0.push(Sym::DEFAULT);
@ -178,15 +176,14 @@ impl ModuleItemList {
ExportDeclaration::Declaration(decl) => {
return BoundNamesVisitor(self.0).visit_declaration(decl);
}
ExportDeclaration::DefaultFunction(f) => f.name(),
ExportDeclaration::DefaultGenerator(g) => g.name(),
ExportDeclaration::DefaultAsyncFunction(af) => af.name(),
ExportDeclaration::DefaultAsyncGenerator(ag) => ag.name(),
ExportDeclaration::DefaultFunctionDeclaration(f) => f.name(),
ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name(),
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name(),
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name(),
ExportDeclaration::DefaultClassDeclaration(cl) => cl.name(),
};
self.0
.insert(name.unwrap_or_else(|| Identifier::new(Sym::DEFAULT_EXPORT)));
self.0.insert(name);
ControlFlow::Continue(())
}
@ -388,23 +385,18 @@ impl ModuleItemList {
}
return ControlFlow::Continue(());
}
ExportDeclaration::DefaultFunction(f) => f.name(),
ExportDeclaration::DefaultGenerator(g) => g.name(),
ExportDeclaration::DefaultAsyncFunction(af) => af.name(),
ExportDeclaration::DefaultAsyncGenerator(ag) => ag.name(),
ExportDeclaration::DefaultFunctionDeclaration(f) => f.name(),
ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name(),
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name(),
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name(),
ExportDeclaration::DefaultClassDeclaration(c) => c.name(),
ExportDeclaration::DefaultAssignmentExpression(_) => {
Some(Identifier::from(Sym::DEFAULT_EXPORT))
Identifier::from(Sym::DEFAULT_EXPORT)
}
};
self.0.push(
LocalExportEntry::new(
name.unwrap_or_else(|| Identifier::from(Sym::DEFAULT_EXPORT)),
Sym::DEFAULT,
)
.into(),
);
self.0
.push(LocalExportEntry::new(name, Sym::DEFAULT).into());
ControlFlow::Continue(())
}

634
core/ast/src/operations.rs

@ -14,14 +14,16 @@ use crate::{
},
expression::{
access::{PrivatePropertyAccess, SuperPropertyAccess},
literal::PropertyDefinition,
operator::BinaryInPrivate,
Await, Identifier, OptionalOperationKind, SuperCall, Yield,
},
function::{
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
Function, Generator,
ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,
ClassElementName, ClassExpression, FormalParameterList, FunctionDeclaration,
FunctionExpression, GeneratorDeclaration, GeneratorExpression,
},
property::{MethodDefinition, PropertyDefinition},
statement::{
iteration::{ForLoopInitializer, IterableLoopInitializer},
LabelledItem,
@ -87,23 +89,81 @@ where
ControlFlow::Continue(())
}
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
fn visit_function_expression(
&mut self,
_: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_function_declaration(
&mut self,
_: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function_expression(
&mut self,
_: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function_declaration(
&mut self,
_: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
fn visit_generator_expression(
&mut self,
_: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
fn visit_generator_declaration(
&mut self,
_: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
fn visit_async_generator_expression(
&mut self,
_: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
fn visit_async_generator_declaration(
&mut self,
_: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
return ControlFlow::Break(());
}
if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
return ControlFlow::Break(());
}
@ -118,9 +178,14 @@ where
// `ComputedPropertyContains`: https://tc39.es/ecma262/#sec-static-semantics-computedpropertycontains
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(name, _)
| ClassElement::StaticMethodDefinition(name, _)
| ClassElement::FieldDefinition(name, _)
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PropertyName(name) = m.name() {
name.visit_with(self)
} else {
ControlFlow::Continue(())
}
}
ClassElement::FieldDefinition(name, _)
| ClassElement::StaticFieldDefinition(name, _) => name.visit_with(self),
_ => ControlFlow::Continue(()),
}
@ -130,11 +195,11 @@ where
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(name, _) = node {
if let PropertyDefinition::MethodDefinition(m) = node {
if self.0 == ContainsSymbol::MethodDefinition {
return ControlFlow::Break(());
}
return name.visit_with(self);
return m.name().visit_with(self);
}
node.visit_with(self)
@ -250,27 +315,67 @@ where
}
}
fn visit_function(&mut self, _: &'ast Function) -> ControlFlow<Self::BreakTy> {
fn visit_function_expression(
&mut self,
_: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_function_declaration(
&mut self,
_: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function(&mut self, _: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_expression(
&mut self,
_: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator(&mut self, _: &'ast Generator) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_declaration(
&mut self,
_: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator(&mut self, _: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
fn visit_generator_expression(
&mut self,
_: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator_declaration(
&mut self,
_: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator_expression(
&mut self,
_: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator_declaration(
&mut self,
_: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(name, _)
| ClassElement::StaticMethodDefinition(name, _) => return name.visit_with(self),
_ => {}
if let ClassElement::MethodDefinition(m) = node {
if let ClassElementName::PropertyName(name) = m.name() {
return name.visit_with(self);
}
}
node.visit_with(self)
}
@ -279,8 +384,8 @@ where
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(name, _) = node {
name.visit_with(self)
if let PropertyDefinition::MethodDefinition(m) = node {
m.name().visit_with(self)
} else {
node.visit_with(self)
}
@ -296,15 +401,8 @@ where
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
#[must_use]
#[inline]
pub fn has_direct_super(method: &MethodDefinition) -> bool {
match method {
MethodDefinition::Get(f) | MethodDefinition::Set(f) | MethodDefinition::Ordinary(f) => {
contains(f, ContainsSymbol::SuperCall)
}
MethodDefinition::Generator(f) => contains(f, ContainsSymbol::SuperCall),
MethodDefinition::AsyncGenerator(f) => contains(f, ContainsSymbol::SuperCall),
MethodDefinition::Async(f) => contains(f, ContainsSymbol::SuperCall),
}
pub fn has_direct_super_new(params: &FormalParameterList, body: &Script) -> bool {
contains(params, ContainsSymbol::SuperCall) || contains(body, ContainsSymbol::SuperCall)
}
/// A container that [`BoundNamesVisitor`] can use to push the found identifiers.
@ -352,41 +450,96 @@ impl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> {
ControlFlow::Continue(())
}
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), true);
}
ControlFlow::Continue(())
}
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), true);
ControlFlow::Continue(())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_export_declaration(
&mut self,
node: &'ast ExportDeclaration,
@ -394,31 +547,20 @@ impl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> {
match node {
ExportDeclaration::VarStatement(var) => try_break!(self.visit_var_declaration(var)),
ExportDeclaration::Declaration(decl) => try_break!(self.visit_declaration(decl)),
ExportDeclaration::DefaultFunction(f) => {
self.0
.add(f.name().map_or(Sym::DEFAULT_EXPORT, Identifier::sym), true);
ExportDeclaration::DefaultFunctionDeclaration(f) => {
self.0.add(f.name().sym(), true);
}
ExportDeclaration::DefaultGenerator(g) => {
self.0
.add(g.name().map_or(Sym::DEFAULT_EXPORT, Identifier::sym), false);
ExportDeclaration::DefaultGeneratorDeclaration(g) => {
self.0.add(g.name().sym(), false);
}
ExportDeclaration::DefaultAsyncFunction(af) => {
self.0.add(
af.name().map_or(Sym::DEFAULT_EXPORT, Identifier::sym),
false,
);
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {
self.0.add(af.name().sym(), false);
}
ExportDeclaration::DefaultAsyncGenerator(ag) => {
self.0.add(
ag.name().map_or(Sym::DEFAULT_EXPORT, Identifier::sym),
false,
);
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {
self.0.add(ag.name().sym(), false);
}
ExportDeclaration::DefaultClassDeclaration(cl) => {
self.0.add(
cl.name().map_or(Sym::DEFAULT_EXPORT, Identifier::sym),
false,
);
self.0.add(cl.name().sym(), false);
}
ExportDeclaration::DefaultAssignmentExpression(_) => {
self.0.add(Sym::DEFAULT_EXPORT, false);
@ -502,24 +644,66 @@ impl<'ast, T: IdentList> Visitor<'ast> for LexicallyDeclaredNamesVisitor<'_, T>
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Function(f) => BoundNamesVisitor(self.0).visit_function(f),
LabelledItem::FunctionDeclaration(f) => {
BoundNamesVisitor(self.0).visit_function_declaration(f)
}
LabelledItem::Statement(_) => ControlFlow::Continue(()),
}
}
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
@ -730,7 +914,7 @@ impl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> {
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Function(_) => ControlFlow::Continue(()),
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
LabelledItem::Statement(stmt) => self.visit(stmt),
}
}
@ -745,19 +929,59 @@ impl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> {
self.visit(node.block())
}
fn visit_function(&mut self, node: &'ast Function) -> ControlFlow<Self::BreakTy> {
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_function(&mut self, node: &'ast AsyncFunction) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_generator(&mut self, node: &'ast Generator) -> ControlFlow<Self::BreakTy> {
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator(&mut self, node: &'ast AsyncGenerator) -> ControlFlow<Self::BreakTy> {
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_script(node.body())
}
@ -815,12 +1039,12 @@ fn top_level_lexicals<T: IdentList>(stmts: &StatementList, names: &mut T) {
// Note
// At the top level of a function, or script, function declarations are treated like
// var declarations rather than like lexical declarations.
Declaration::Function(_)
| Declaration::Generator(_)
| Declaration::AsyncFunction(_)
| Declaration::AsyncGenerator(_) => {}
Declaration::Class(class) => {
BoundNamesVisitor(names).visit_class(class);
Declaration::FunctionDeclaration(_)
| Declaration::GeneratorDeclaration(_)
| Declaration::AsyncFunctionDeclaration(_)
| Declaration::AsyncGeneratorDeclaration(_) => {}
Declaration::ClassDeclaration(class) => {
BoundNamesVisitor(names).visit_class_declaration(class);
}
Declaration::Lexical(decl) => {
BoundNamesVisitor(names).visit_lexical_declaration(decl);
@ -843,27 +1067,27 @@ fn top_level_vars(stmts: &StatementList, names: &mut FxHashSet<Identifier>) {
// Note
// At the top level of a function, or script, function declarations are treated like
// var declarations rather than like lexical declarations.
Declaration::Function(f) => {
BoundNamesVisitor(names).visit_function(f);
Declaration::FunctionDeclaration(f) => {
BoundNamesVisitor(names).visit_function_declaration(f);
}
Declaration::Generator(f) => {
BoundNamesVisitor(names).visit_generator(f);
Declaration::GeneratorDeclaration(f) => {
BoundNamesVisitor(names).visit_generator_declaration(f);
}
Declaration::AsyncFunction(f) => {
BoundNamesVisitor(names).visit_async_function(f);
Declaration::AsyncFunctionDeclaration(f) => {
BoundNamesVisitor(names).visit_async_function_declaration(f);
}
Declaration::AsyncGenerator(f) => {
BoundNamesVisitor(names).visit_async_generator(f);
Declaration::AsyncGeneratorDeclaration(f) => {
BoundNamesVisitor(names).visit_async_generator_declaration(f);
}
Declaration::Class(_) | Declaration::Lexical(_) => {}
Declaration::ClassDeclaration(_) | Declaration::Lexical(_) => {}
}
}
StatementListItem::Statement(stmt) => {
let mut stmt = Some(stmt);
while let Some(Statement::Labelled(labelled)) = stmt {
match labelled.item() {
LabelledItem::Function(f) => {
BoundNamesVisitor(names).visit_function(f);
LabelledItem::FunctionDeclaration(f) => {
BoundNamesVisitor(names).visit_function_declaration(f);
stmt = None;
}
LabelledItem::Statement(s) => stmt = Some(s),
@ -898,7 +1122,10 @@ struct AllPrivateIdentifiersValidVisitor(Vec<Sym>);
impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
type BreakTy = ();
fn visit_class(&mut self, node: &'ast Class) -> ControlFlow<Self::BreakTy> {
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.super_ref() {
try_break!(self.visit(node));
}
@ -906,9 +1133,12 @@ impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
let mut names = self.0.clone();
for element in node.elements() {
match element {
ClassElement::PrivateMethodDefinition(name, _)
| ClassElement::PrivateStaticMethodDefinition(name, _)
| ClassElement::PrivateFieldDefinition(name, _)
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PrivateName(name) = m.name() {
names.push(name.description());
}
}
ClassElement::PrivateFieldDefinition(name, _)
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
names.push(name.description());
}
@ -924,10 +1154,12 @@ impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
for element in node.elements() {
match element {
ClassElement::MethodDefinition(name, method)
| ClassElement::StaticMethodDefinition(name, method) => {
try_break!(visitor.visit(name));
try_break!(visitor.visit(method));
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PropertyName(name) = m.name() {
try_break!(visitor.visit(name));
}
try_break!(visitor.visit(m.parameters()));
try_break!(visitor.visit(m.body()));
}
ClassElement::FieldDefinition(name, expression)
| ClassElement::StaticFieldDefinition(name, expression) => {
@ -936,9 +1168,66 @@ impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
try_break!(visitor.visit(expression));
}
}
ClassElement::PrivateMethodDefinition(_, method)
| ClassElement::PrivateStaticMethodDefinition(_, method) => {
try_break!(visitor.visit(method));
ClassElement::PrivateFieldDefinition(_, expression)
| ClassElement::PrivateStaticFieldDefinition(_, expression) => {
if let Some(expression) = expression {
try_break!(visitor.visit(expression));
}
}
ClassElement::StaticBlock(statement_list) => {
try_break!(visitor.visit(statement_list));
}
}
}
ControlFlow::Continue(())
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.super_ref() {
try_break!(self.visit(node));
}
let mut names = self.0.clone();
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PrivateName(name) = m.name() {
names.push(name.description());
}
}
ClassElement::PrivateFieldDefinition(name, _)
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
names.push(name.description());
}
_ => {}
}
}
let mut visitor = Self(names);
if let Some(node) = node.constructor() {
try_break!(visitor.visit(node));
}
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PropertyName(name) = m.name() {
try_break!(visitor.visit(name));
}
try_break!(visitor.visit(m.parameters()));
try_break!(visitor.visit(m.body()));
}
ClassElement::FieldDefinition(name, expression)
| ClassElement::StaticFieldDefinition(name, expression) => {
try_break!(visitor.visit(name));
if let Some(expression) = expression {
try_break!(visitor.visit(expression));
}
}
ClassElement::PrivateFieldDefinition(_, expression)
| ClassElement::PrivateStaticFieldDefinition(_, expression) => {
@ -1297,7 +1586,7 @@ where
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Statement(stmt) => self.visit_statement(stmt),
LabelledItem::Function(_) => ControlFlow::Continue(()),
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
}
}
@ -1388,20 +1677,20 @@ pub enum LexicallyScopedDeclaration<'a> {
/// See [`LexicalDeclaration`]
LexicalDeclaration(&'a LexicalDeclaration),
/// See [`Function`]
Function(&'a Function),
/// See [`FunctionDeclaration`]
FunctionDeclaration(&'a FunctionDeclaration),
/// See [`Generator`]
Generator(&'a Generator),
/// See [`GeneratorDeclaration`]
GeneratorDeclaration(&'a GeneratorDeclaration),
/// See [`AsyncFunction`]
AsyncFunction(&'a AsyncFunction),
/// See [`AsyncFunctionDeclaration`]
AsyncFunctionDeclaration(&'a AsyncFunctionDeclaration),
/// See [`AsyncGenerator`]
AsyncGenerator(&'a AsyncGenerator),
/// See [`AsyncGeneratorDeclaration`]
AsyncGeneratorDeclaration(&'a AsyncGeneratorDeclaration),
/// See [`Class`]
Class(&'a Class),
/// See [`ClassDeclaration`]
ClassDeclaration(&'a ClassDeclaration),
/// A default assignment expression as an export declaration.
///
@ -1415,11 +1704,11 @@ impl LexicallyScopedDeclaration<'_> {
pub fn bound_names(&self) -> Vec<Identifier> {
match *self {
Self::LexicalDeclaration(v) => bound_names(v),
Self::Function(f) => bound_names(f),
Self::Generator(g) => bound_names(g),
Self::AsyncFunction(f) => bound_names(f),
Self::AsyncGenerator(g) => bound_names(g),
Self::Class(cl) => bound_names(cl),
Self::FunctionDeclaration(f) => bound_names(f),
Self::GeneratorDeclaration(g) => bound_names(g),
Self::AsyncFunctionDeclaration(f) => bound_names(f),
Self::AsyncGeneratorDeclaration(g) => bound_names(g),
Self::ClassDeclaration(cl) => bound_names(cl),
Self::AssignmentExpression(expr) => bound_names(expr),
}
}
@ -1428,11 +1717,11 @@ impl LexicallyScopedDeclaration<'_> {
impl<'ast> From<&'ast Declaration> for LexicallyScopedDeclaration<'ast> {
fn from(value: &'ast Declaration) -> LexicallyScopedDeclaration<'ast> {
match value {
Declaration::Function(f) => Self::Function(f),
Declaration::Generator(g) => Self::Generator(g),
Declaration::AsyncFunction(af) => Self::AsyncFunction(af),
Declaration::AsyncGenerator(ag) => Self::AsyncGenerator(ag),
Declaration::Class(c) => Self::Class(c),
Declaration::FunctionDeclaration(f) => Self::FunctionDeclaration(f),
Declaration::GeneratorDeclaration(g) => Self::GeneratorDeclaration(g),
Declaration::AsyncFunctionDeclaration(af) => Self::AsyncFunctionDeclaration(af),
Declaration::AsyncGeneratorDeclaration(ag) => Self::AsyncGeneratorDeclaration(ag),
Declaration::ClassDeclaration(c) => Self::ClassDeclaration(c),
Declaration::Lexical(lex) => Self::LexicalDeclaration(lex),
}
}
@ -1490,19 +1779,23 @@ impl<'ast> Visitor<'ast> for LexicallyScopedDeclarationsVisitor<'_, 'ast> {
// ExportDeclaration : export default HoistableDeclaration
// 1. Return a List whose sole element is DeclarationPart of HoistableDeclaration.
ExportDeclaration::DefaultFunction(f) => LexicallyScopedDeclaration::Function(f),
ExportDeclaration::DefaultGenerator(g) => LexicallyScopedDeclaration::Generator(g),
ExportDeclaration::DefaultAsyncFunction(af) => {
LexicallyScopedDeclaration::AsyncFunction(af)
ExportDeclaration::DefaultFunctionDeclaration(f) => {
LexicallyScopedDeclaration::FunctionDeclaration(f)
}
ExportDeclaration::DefaultGeneratorDeclaration(g) => {
LexicallyScopedDeclaration::GeneratorDeclaration(g)
}
ExportDeclaration::DefaultAsyncGenerator(ag) => {
LexicallyScopedDeclaration::AsyncGenerator(ag)
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {
LexicallyScopedDeclaration::AsyncFunctionDeclaration(af)
}
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {
LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag)
}
// ExportDeclaration : export default ClassDeclaration
ExportDeclaration::DefaultClassDeclaration(c) => {
// 1. Return a List whose sole element is ClassDeclaration.
LexicallyScopedDeclaration::Class(c)
LexicallyScopedDeclaration::ClassDeclaration(c)
}
// ExportDeclaration : export default AssignmentExpression ;
@ -1544,9 +1837,10 @@ impl<'ast> Visitor<'ast> for LexicallyScopedDeclarationsVisitor<'_, 'ast> {
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
// LabelledItem : FunctionDeclaration
LabelledItem::Function(f) => {
LabelledItem::FunctionDeclaration(f) => {
// 1. Return « FunctionDeclaration ».
self.0.push(LexicallyScopedDeclaration::Function(f));
self.0
.push(LexicallyScopedDeclaration::FunctionDeclaration(f));
}
// LabelledItem : Statement
@ -1591,16 +1885,17 @@ impl<'ast> Visitor<'ast> for TopLevelLexicallyScopedDeclarationsVisitor<'_, 'ast
// StatementListItem : Declaration
StatementListItem::Declaration(d) => match d {
// 1. If Declaration is Declaration : HoistableDeclaration , then
Declaration::Function(_)
| Declaration::Generator(_)
| Declaration::AsyncFunction(_)
| Declaration::AsyncGenerator(_) => {
Declaration::FunctionDeclaration(_)
| Declaration::GeneratorDeclaration(_)
| Declaration::AsyncFunctionDeclaration(_)
| Declaration::AsyncGeneratorDeclaration(_) => {
// a. Return a new empty List.
}
// 2. Return « Declaration ».
Declaration::Class(cl) => {
self.0.push(LexicallyScopedDeclaration::Class(cl));
Declaration::ClassDeclaration(cl) => {
self.0
.push(LexicallyScopedDeclaration::ClassDeclaration(cl));
}
Declaration::Lexical(lex) => {
self.0
@ -1624,17 +1919,17 @@ pub enum VarScopedDeclaration {
/// See [`VarDeclaration`]
VariableDeclaration(Variable),
/// See [`Function`]
Function(Function),
/// See [`FunctionDeclaration`]
FunctionDeclaration(FunctionDeclaration),
/// See [`Generator`]
Generator(Generator),
/// See [`GeneratorDeclaration`]
GeneratorDeclaration(GeneratorDeclaration),
/// See [`AsyncFunction`]
AsyncFunction(AsyncFunction),
/// See [`AsyncFunctionDeclaration`]
AsyncFunctionDeclaration(AsyncFunctionDeclaration),
/// See [`AsyncGenerator`]
AsyncGenerator(AsyncGenerator),
/// See [`AsyncGeneratorDeclaration`]
AsyncGeneratorDeclaration(AsyncGeneratorDeclaration),
}
impl VarScopedDeclaration {
@ -1643,10 +1938,10 @@ impl VarScopedDeclaration {
pub fn bound_names(&self) -> Vec<Identifier> {
match self {
Self::VariableDeclaration(v) => bound_names(v),
Self::Function(f) => bound_names(f),
Self::Generator(g) => bound_names(g),
Self::AsyncFunction(f) => bound_names(f),
Self::AsyncGenerator(g) => bound_names(g),
Self::FunctionDeclaration(f) => bound_names(f),
Self::GeneratorDeclaration(g) => bound_names(g),
Self::AsyncFunctionDeclaration(f) => bound_names(f),
Self::AsyncGeneratorDeclaration(g) => bound_names(g),
}
}
}
@ -1802,7 +2097,7 @@ impl<'ast> Visitor<'ast> for VarScopedDeclarationsVisitor<'_> {
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Statement(s) => self.visit(s),
LabelledItem::Function(_) => ControlFlow::Continue(()),
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
}
}
@ -1851,17 +2146,21 @@ impl<'ast> Visitor<'ast> for TopLevelVarScopedDeclarationsVisitor<'_> {
match node {
StatementListItem::Declaration(d) => {
match d {
Declaration::Function(f) => {
self.0.push(VarScopedDeclaration::Function(f.clone()));
Declaration::FunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::FunctionDeclaration(f.clone()));
}
Declaration::Generator(f) => {
self.0.push(VarScopedDeclaration::Generator(f.clone()));
Declaration::GeneratorDeclaration(f) => {
self.0
.push(VarScopedDeclaration::GeneratorDeclaration(f.clone()));
}
Declaration::AsyncFunction(f) => {
self.0.push(VarScopedDeclaration::AsyncFunction(f.clone()));
Declaration::AsyncFunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::AsyncFunctionDeclaration(f.clone()));
}
Declaration::AsyncGenerator(f) => {
self.0.push(VarScopedDeclaration::AsyncGenerator(f.clone()));
Declaration::AsyncGeneratorDeclaration(f) => {
self.0
.push(VarScopedDeclaration::AsyncGeneratorDeclaration(f.clone()));
}
_ => {}
}
@ -1882,8 +2181,9 @@ impl<'ast> Visitor<'ast> for TopLevelVarScopedDeclarationsVisitor<'_> {
VarScopedDeclarationsVisitor(self.0).visit(s);
ControlFlow::Continue(())
}
LabelledItem::Function(f) => {
self.0.push(VarScopedDeclaration::Function(f.clone()));
LabelledItem::FunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::FunctionDeclaration(f.clone()));
ControlFlow::Continue(())
}
}
@ -1949,10 +2249,10 @@ impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
fn visit_block(&mut self, node: &'ast crate::statement::Block) -> ControlFlow<Self::BreakTy> {
self.visit(node.statement_list());
for statement in node.statement_list().statements() {
if let StatementListItem::Declaration(Declaration::Function(function)) = statement {
let name = function
.name()
.expect("function declaration must have name");
if let StatementListItem::Declaration(Declaration::FunctionDeclaration(function)) =
statement
{
let name = function.name();
self.0.push(name);
}
}
@ -1969,10 +2269,10 @@ impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
for case in node.cases() {
self.visit(case);
for statement in case.body().statements() {
if let StatementListItem::Declaration(Declaration::Function(function)) = statement {
let name = function
.name()
.expect("function declaration must have name");
if let StatementListItem::Declaration(Declaration::FunctionDeclaration(function)) =
statement
{
let name = function.name();
self.0.push(name);
}
}
@ -1980,10 +2280,10 @@ impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
if let Some(default) = node.default() {
self.visit(default);
for statement in default.statements() {
if let StatementListItem::Declaration(Declaration::Function(function)) = statement {
let name = function
.name()
.expect("function declaration must have name");
if let StatementListItem::Declaration(Declaration::FunctionDeclaration(function)) =
statement
{
let name = function.name();
self.0.push(name);
}
}
@ -2152,7 +2452,7 @@ impl<'ast> Visitor<'ast> for ReturnsValueVisitor {
) -> ControlFlow<Self::BreakTy> {
match node.item() {
LabelledItem::Statement(node) => try_break!(self.visit(node)),
LabelledItem::Function(_) => {}
LabelledItem::FunctionDeclaration(_) => {}
}
ControlFlow::Continue(())
}

280
core/ast/src/property.rs

@ -1,245 +1,10 @@
//! Property definition related types, used in object literals and class definitions.
use crate::function::PrivateName;
use crate::try_break;
use super::{expression::literal::Literal, Expression};
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
use super::{
expression::{literal::Literal, Identifier},
function::{AsyncFunction, AsyncGenerator, Function, Generator},
Expression,
};
/// Describes the definition of a property within an object literal.
///
/// A property has a name (a string) and a value (primitive, method, or object reference).
/// Note that when we say that "a property holds an object", that is shorthand for "a property holds an object reference".
/// This distinction matters because the original referenced object remains unchanged when you change the property's value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyDefinition {
/// Puts a variable into an object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions
IdentifierReference(Identifier),
/// Binds a property name to a JavaScript value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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, Expression),
/// A property of an object can also refer to a function or a getter or setter method.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions
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.
///
/// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties
SpreadObject(Expression),
/// Cover grammar for when an object literal is used as an object binding pattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName
CoverInitializedName(Identifier, Expression),
}
impl VisitWith for PropertyDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::IdentifierReference(id) => visitor.visit_identifier(id),
Self::Property(pn, expr) => {
try_break!(visitor.visit_property_name(pn));
visitor.visit_expression(expr)
}
Self::MethodDefinition(pn, md) => {
try_break!(visitor.visit_property_name(pn));
visitor.visit_method_definition(md)
}
Self::SpreadObject(expr) => visitor.visit_expression(expr),
Self::CoverInitializedName(id, expr) => {
try_break!(visitor.visit_identifier(id));
visitor.visit_expression(expr)
}
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::IdentifierReference(id) => visitor.visit_identifier_mut(id),
Self::Property(pn, expr) => {
try_break!(visitor.visit_property_name_mut(pn));
visitor.visit_expression_mut(expr)
}
Self::MethodDefinition(pn, md) => {
try_break!(visitor.visit_property_name_mut(pn));
visitor.visit_method_definition_mut(md)
}
Self::SpreadObject(expr) => visitor.visit_expression_mut(expr),
Self::CoverInitializedName(id, expr) => {
try_break!(visitor.visit_identifier_mut(id));
visitor.visit_expression_mut(expr)
}
}
}
}
/// Method definition.
///
/// Starting with ECMAScript 2015, a shorter syntax for method definitions on objects initializers is introduced.
/// It is a shorthand for a function assigned to the method's name.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[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.
///
/// Sometimes it is desirable to allow access to a property that returns a dynamically computed value,
/// or you may want to reflect the status of an internal variable without requiring the use of explicit method calls.
/// In JavaScript, this can be accomplished with the use of a getter.
///
/// It is not possible to simultaneously have a getter bound to a property and have that property actually hold a value,
/// although it is possible to use a getter and a setter in conjunction to create a type of pseudo-property.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
Get(Function),
/// The `set` syntax binds an object property to a function to be called when there is an attempt to set that property.
///
/// In JavaScript, a setter can be used to execute a function whenever a specified property is attempted to be changed.
/// Setters are most often used in conjunction with getters to create a type of pseudo-property.
/// It is not possible to simultaneously have a setter on a property that holds an actual value.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
Set(Function),
/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Method_definition_syntax
Ordinary(Function),
/// Starting with ECMAScript 2015, you are able to define own methods in a shorter syntax, similar to the getters and setters.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods
Generator(Generator),
/// Async generators can be used to define a method
///
/// More information
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [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(AsyncGenerator),
/// Async function can be used to define a method
///
/// More information
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_methods
Async(AsyncFunction),
}
impl VisitWith for MethodDefinition {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function(f),
Self::Generator(g) => visitor.visit_generator(g),
Self::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
Self::Async(af) => visitor.visit_async_function(af),
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::Get(f) | Self::Set(f) | Self::Ordinary(f) => visitor.visit_function_mut(f),
Self::Generator(g) => visitor.visit_generator_mut(g),
Self::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Self::Async(af) => visitor.visit_async_function_mut(af),
}
}
}
/// `PropertyName` can be either a literal or computed.
///
/// More information:
@ -343,29 +108,26 @@ impl VisitWith for PropertyName {
}
}
/// `ClassElementName` can be either a property name or a private identifier.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName
/// The kind of a method definition.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum ClassElementName {
/// A public property.
PropertyName(PropertyName),
/// A private property.
PrivateIdentifier(PrivateName),
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MethodDefinitionKind {
/// A getter method.
Get,
impl ClassElementName {
/// Returns the property name if it exists.
#[must_use]
pub const fn literal(&self) -> Option<Sym> {
if let Self::PropertyName(name) = self {
name.literal()
} else {
None
}
}
/// A setter method.
Set,
/// An ordinary method.
Ordinary,
/// A generator method.
Generator,
/// An async generator method.
AsyncGenerator,
/// An async method.
Async,
}

21
core/ast/src/statement/labelled.rs

@ -1,5 +1,5 @@
use crate::{
function::Function,
function::FunctionDeclaration,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
Statement,
@ -12,7 +12,7 @@ use core::ops::ControlFlow;
/// Semantically, a [`Labelled`] statement should only wrap [`Statement`] nodes. However,
/// old ECMAScript implementations supported [labelled function declarations][label-fn] as an extension
/// of the grammar. In the ECMAScript 2015 spec, the production of `LabelledStatement` was
/// modified to include labelled [`Function`]s as a valid node.
/// modified to include labelled [`FunctionDeclaration`]s as a valid node.
///
/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem
/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations
@ -20,8 +20,9 @@ use core::ops::ControlFlow;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum LabelledItem {
/// A labelled [`Function`].
Function(Function),
/// A labelled [`FunctionDeclaration`].
FunctionDeclaration(FunctionDeclaration),
/// A labelled [`Statement`].
Statement(Statement),
}
@ -29,7 +30,7 @@ pub enum LabelledItem {
impl LabelledItem {
pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
match self {
Self::Function(f) => f.to_indented_string(interner, indentation),
Self::FunctionDeclaration(f) => f.to_indented_string(interner, indentation),
Self::Statement(stmt) => stmt.to_indented_string(interner, indentation),
}
}
@ -41,9 +42,9 @@ impl ToInternedString for LabelledItem {
}
}
impl From<Function> for LabelledItem {
fn from(f: Function) -> Self {
Self::Function(f)
impl From<FunctionDeclaration> for LabelledItem {
fn from(f: FunctionDeclaration) -> Self {
Self::FunctionDeclaration(f)
}
}
@ -59,7 +60,7 @@ impl VisitWith for LabelledItem {
V: Visitor<'a>,
{
match self {
Self::Function(f) => visitor.visit_function(f),
Self::FunctionDeclaration(f) => visitor.visit_function_declaration(f),
Self::Statement(s) => visitor.visit_statement(s),
}
}
@ -69,7 +70,7 @@ impl VisitWith for LabelledItem {
V: VisitorMut<'a>,
{
match self {
Self::Function(f) => visitor.visit_function_mut(f),
Self::FunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),
Self::Statement(s) => visitor.visit_statement_mut(s),
}
}

2
core/ast/src/statement/mod.rs

@ -156,7 +156,7 @@ impl Statement {
pub fn is_labelled_function(&self) -> bool {
match self {
Self::Labelled(stmt) => match stmt.item() {
LabelledItem::Function(_) => true,
LabelledItem::FunctionDeclaration(_) => true,
LabelledItem::Statement(stmt) => stmt.is_labelled_function(),
},
_ => false,

109
core/ast/src/visitor.rs

@ -16,7 +16,10 @@ use crate::{
PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess,
SuperPropertyAccess,
},
literal::{ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral},
literal::{
ArrayLiteral, Literal, ObjectLiteral, ObjectMethodDefinition, PropertyDefinition,
TemplateElement, TemplateLiteral,
},
operator::{
assign::{Assign, AssignTarget},
Binary, BinaryInPrivate, Conditional, Unary, Update,
@ -26,11 +29,13 @@ use crate::{
Yield,
},
function::{
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
FormalParameter, FormalParameterList, Function, Generator, PrivateName,
ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,
ClassExpression, FormalParameter, FormalParameterList, FunctionDeclaration,
FunctionExpression, GeneratorDeclaration, GeneratorExpression, PrivateName,
},
pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern},
property::{MethodDefinition, PropertyDefinition, PropertyName},
property::PropertyName,
statement::{
iteration::{
Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop,
@ -125,11 +130,16 @@ node_ref! {
StatementListItem,
Statement,
Declaration,
Function,
Generator,
AsyncFunction,
AsyncGenerator,
Class,
FunctionExpression,
FunctionDeclaration,
GeneratorExpression,
GeneratorDeclaration,
AsyncFunctionExpression,
AsyncFunctionDeclaration,
AsyncGeneratorExpression,
AsyncGeneratorDeclaration,
ClassExpression,
ClassDeclaration,
LexicalDeclaration,
Block,
VarDeclaration,
@ -189,7 +199,7 @@ node_ref! {
Finally,
FormalParameter,
PropertyName,
MethodDefinition,
ObjectMethodDefinition,
ObjectPattern,
ArrayPattern,
PropertyDefinition,
@ -228,11 +238,16 @@ pub trait Visitor<'ast>: Sized {
define_visit!(visit_statement_list_item, StatementListItem);
define_visit!(visit_statement, Statement);
define_visit!(visit_declaration, Declaration);
define_visit!(visit_function, Function);
define_visit!(visit_generator, Generator);
define_visit!(visit_async_function, AsyncFunction);
define_visit!(visit_async_generator, AsyncGenerator);
define_visit!(visit_class, Class);
define_visit!(visit_function_expression, FunctionExpression);
define_visit!(visit_function_declaration, FunctionDeclaration);
define_visit!(visit_generator_expression, GeneratorExpression);
define_visit!(visit_generator_declaration, GeneratorDeclaration);
define_visit!(visit_async_function_expression, AsyncFunctionExpression);
define_visit!(visit_async_function_declaration, AsyncFunctionDeclaration);
define_visit!(visit_async_generator_expression, AsyncGeneratorExpression);
define_visit!(visit_async_generator_declaration, AsyncGeneratorDeclaration);
define_visit!(visit_class_expression, ClassExpression);
define_visit!(visit_class_declaration, ClassDeclaration);
define_visit!(visit_lexical_declaration, LexicalDeclaration);
define_visit!(visit_block, Block);
define_visit!(visit_var_declaration, VarDeclaration);
@ -292,7 +307,7 @@ pub trait Visitor<'ast>: Sized {
define_visit!(visit_finally, Finally);
define_visit!(visit_formal_parameter, FormalParameter);
define_visit!(visit_property_name, PropertyName);
define_visit!(visit_method_definition, MethodDefinition);
define_visit!(visit_object_method_definition, ObjectMethodDefinition);
define_visit!(visit_object_pattern, ObjectPattern);
define_visit!(visit_array_pattern, ArrayPattern);
define_visit!(visit_property_definition, PropertyDefinition);
@ -328,11 +343,16 @@ pub trait Visitor<'ast>: Sized {
NodeRef::StatementListItem(n) => self.visit_statement_list_item(n),
NodeRef::Statement(n) => self.visit_statement(n),
NodeRef::Declaration(n) => self.visit_declaration(n),
NodeRef::Function(n) => self.visit_function(n),
NodeRef::Generator(n) => self.visit_generator(n),
NodeRef::AsyncFunction(n) => self.visit_async_function(n),
NodeRef::AsyncGenerator(n) => self.visit_async_generator(n),
NodeRef::Class(n) => self.visit_class(n),
NodeRef::FunctionExpression(n) => self.visit_function_expression(n),
NodeRef::FunctionDeclaration(n) => self.visit_function_declaration(n),
NodeRef::GeneratorExpression(n) => self.visit_generator_expression(n),
NodeRef::GeneratorDeclaration(n) => self.visit_generator_declaration(n),
NodeRef::AsyncFunctionExpression(n) => self.visit_async_function_expression(n),
NodeRef::AsyncFunctionDeclaration(n) => self.visit_async_function_declaration(n),
NodeRef::AsyncGeneratorExpression(n) => self.visit_async_generator_expression(n),
NodeRef::AsyncGeneratorDeclaration(n) => self.visit_async_generator_declaration(n),
NodeRef::ClassExpression(n) => self.visit_class_expression(n),
NodeRef::ClassDeclaration(n) => self.visit_class_declaration(n),
NodeRef::LexicalDeclaration(n) => self.visit_lexical_declaration(n),
NodeRef::Block(n) => self.visit_block(n),
NodeRef::VarDeclaration(n) => self.visit_var_declaration(n),
@ -392,7 +412,7 @@ pub trait Visitor<'ast>: Sized {
NodeRef::Finally(n) => self.visit_finally(n),
NodeRef::FormalParameter(n) => self.visit_formal_parameter(n),
NodeRef::PropertyName(n) => self.visit_property_name(n),
NodeRef::MethodDefinition(n) => self.visit_method_definition(n),
NodeRef::ObjectMethodDefinition(n) => self.visit_object_method_definition(n),
NodeRef::ObjectPattern(n) => self.visit_object_pattern(n),
NodeRef::ArrayPattern(n) => self.visit_array_pattern(n),
NodeRef::PropertyDefinition(n) => self.visit_property_definition(n),
@ -433,11 +453,25 @@ pub trait VisitorMut<'ast>: Sized {
define_visit_mut!(visit_statement_list_item_mut, StatementListItem);
define_visit_mut!(visit_statement_mut, Statement);
define_visit_mut!(visit_declaration_mut, Declaration);
define_visit_mut!(visit_function_mut, Function);
define_visit_mut!(visit_generator_mut, Generator);
define_visit_mut!(visit_async_function_mut, AsyncFunction);
define_visit_mut!(visit_async_generator_mut, AsyncGenerator);
define_visit_mut!(visit_class_mut, Class);
define_visit_mut!(visit_function_expression_mut, FunctionExpression);
define_visit_mut!(visit_function_declaration_mut, FunctionDeclaration);
define_visit_mut!(visit_generator_expression_mut, GeneratorExpression);
define_visit_mut!(visit_generator_declaration_mut, GeneratorDeclaration);
define_visit_mut!(visit_async_function_expression_mut, AsyncFunctionExpression);
define_visit_mut!(
visit_async_function_declaration_mut,
AsyncFunctionDeclaration
);
define_visit_mut!(
visit_async_generator_expression_mut,
AsyncGeneratorExpression
);
define_visit_mut!(
visit_async_generator_declaration_mut,
AsyncGeneratorDeclaration
);
define_visit_mut!(visit_class_expression_mut, ClassExpression);
define_visit_mut!(visit_class_declaration_mut, ClassDeclaration);
define_visit_mut!(visit_lexical_declaration_mut, LexicalDeclaration);
define_visit_mut!(visit_block_mut, Block);
define_visit_mut!(visit_var_declaration_mut, VarDeclaration);
@ -497,7 +531,7 @@ pub trait VisitorMut<'ast>: Sized {
define_visit_mut!(visit_finally_mut, Finally);
define_visit_mut!(visit_formal_parameter_mut, FormalParameter);
define_visit_mut!(visit_property_name_mut, PropertyName);
define_visit_mut!(visit_method_definition_mut, MethodDefinition);
define_visit_mut!(visit_object_method_definition_mut, ObjectMethodDefinition);
define_visit_mut!(visit_object_pattern_mut, ObjectPattern);
define_visit_mut!(visit_array_pattern_mut, ArrayPattern);
define_visit_mut!(visit_property_definition_mut, PropertyDefinition);
@ -533,11 +567,18 @@ pub trait VisitorMut<'ast>: Sized {
NodeRefMut::StatementListItem(n) => self.visit_statement_list_item_mut(n),
NodeRefMut::Statement(n) => self.visit_statement_mut(n),
NodeRefMut::Declaration(n) => self.visit_declaration_mut(n),
NodeRefMut::Function(n) => self.visit_function_mut(n),
NodeRefMut::Generator(n) => self.visit_generator_mut(n),
NodeRefMut::AsyncFunction(n) => self.visit_async_function_mut(n),
NodeRefMut::AsyncGenerator(n) => self.visit_async_generator_mut(n),
NodeRefMut::Class(n) => self.visit_class_mut(n),
NodeRefMut::FunctionExpression(n) => self.visit_function_expression_mut(n),
NodeRefMut::FunctionDeclaration(n) => self.visit_function_declaration_mut(n),
NodeRefMut::GeneratorExpression(n) => self.visit_generator_expression_mut(n),
NodeRefMut::GeneratorDeclaration(n) => self.visit_generator_declaration_mut(n),
NodeRefMut::AsyncFunctionExpression(n) => self.visit_async_function_expression_mut(n),
NodeRefMut::AsyncFunctionDeclaration(n) => self.visit_async_function_declaration_mut(n),
NodeRefMut::AsyncGeneratorExpression(n) => self.visit_async_generator_expression_mut(n),
NodeRefMut::AsyncGeneratorDeclaration(n) => {
self.visit_async_generator_declaration_mut(n)
}
NodeRefMut::ClassExpression(n) => self.visit_class_expression_mut(n),
NodeRefMut::ClassDeclaration(n) => self.visit_class_declaration_mut(n),
NodeRefMut::LexicalDeclaration(n) => self.visit_lexical_declaration_mut(n),
NodeRefMut::Block(n) => self.visit_block_mut(n),
NodeRefMut::VarDeclaration(n) => self.visit_var_declaration_mut(n),
@ -597,7 +638,7 @@ pub trait VisitorMut<'ast>: Sized {
NodeRefMut::Finally(n) => self.visit_finally_mut(n),
NodeRefMut::FormalParameter(n) => self.visit_formal_parameter_mut(n),
NodeRefMut::PropertyName(n) => self.visit_property_name_mut(n),
NodeRefMut::MethodDefinition(n) => self.visit_method_definition_mut(n),
NodeRefMut::ObjectMethodDefinition(n) => self.visit_object_method_definition_mut(n),
NodeRefMut::ObjectPattern(n) => self.visit_object_pattern_mut(n),
NodeRefMut::ArrayPattern(n) => self.visit_array_pattern_mut(n),
NodeRefMut::PropertyDefinition(n) => self.visit_property_definition_mut(n),

14
core/engine/src/builtins/function/mod.rs

@ -140,7 +140,7 @@ impl ConstructorKind {
#[derive(Clone, Debug, Finalize)]
pub enum ClassFieldDefinition {
/// A class field definition with a `string` or `symbol` as a name.
Public(PropertyKey, JsFunction),
Public(PropertyKey, JsFunction, Option<PropertyKey>),
/// A class field definition with a private name.
Private(PrivateName, JsFunction),
@ -149,7 +149,7 @@ pub enum ClassFieldDefinition {
unsafe impl Trace for ClassFieldDefinition {
custom_trace! {this, mark, {
match this {
Self::Public(_key, func) => {
Self::Public(_key, func, _) => {
mark(func);
}
Self::Private(_, func) => {
@ -265,8 +265,14 @@ impl OrdinaryFunction {
}
/// Pushes a value to the `[[Fields]]` internal slot if present.
pub(crate) fn push_field(&mut self, key: PropertyKey, value: JsFunction) {
self.fields.push(ClassFieldDefinition::Public(key, value));
pub(crate) fn push_field(
&mut self,
key: PropertyKey,
value: JsFunction,
function_name: Option<PropertyKey>,
) {
self.fields
.push(ClassFieldDefinition::Public(key, value, function_name));
}
/// Pushes a private value to the `[[Fields]]` internal slot if present.

464
core/engine/src/bytecompiler/class.rs

@ -5,8 +5,12 @@ use crate::{
};
use boa_ast::{
expression::Identifier,
function::{Class, ClassElement, FormalParameterList},
property::{MethodDefinition, PropertyName},
function::{
ClassDeclaration, ClassElement, ClassElementName, ClassExpression, FormalParameterList,
FunctionExpression,
},
property::{MethodDefinitionKind, PropertyName},
Expression,
};
use boa_gc::Gc;
use boa_interner::Sym;
@ -16,8 +20,42 @@ enum StaticElement {
// A static class block with it's function code.
StaticBlock(Gc<CodeBlock>),
// A static class field with it's function code and optional name index.
StaticField((Gc<CodeBlock>, Option<u32>)),
// A static class field with it's function code, an optional name index and the information if the function is an anonymous function.
StaticField((Gc<CodeBlock>, Option<u32>, bool)),
}
/// Describes the complete specification of a class.
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct ClassSpec<'a> {
name: Option<Identifier>,
super_ref: Option<&'a Expression>,
constructor: Option<&'a FunctionExpression>,
elements: &'a [ClassElement],
has_binding_identifier: bool,
}
impl<'a> From<&'a ClassDeclaration> for ClassSpec<'a> {
fn from(class: &'a ClassDeclaration) -> Self {
Self {
name: Some(class.name()),
super_ref: class.super_ref(),
constructor: class.constructor(),
elements: class.elements(),
has_binding_identifier: true,
}
}
}
impl<'a> From<&'a ClassExpression> for ClassSpec<'a> {
fn from(class: &'a ClassExpression) -> Self {
Self {
name: class.name(),
super_ref: class.super_ref(),
constructor: class.constructor(),
elements: class.elements(),
has_binding_identifier: class.name().is_some(),
}
}
}
impl ByteCompiler<'_> {
@ -26,18 +64,18 @@ impl ByteCompiler<'_> {
/// The compilation of a class declaration and expression is mostly equal.
/// 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.
pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) {
pub(crate) fn compile_class(&mut self, class: ClassSpec<'_>, expression: bool) {
// 11.2.2 Strict Mode Code - <https://tc39.es/ecma262/#sec-strict-mode-code>
// - All parts of a ClassDeclaration or a ClassExpression are strict mode code.
let strict = self.strict();
self.code_block_flags |= CodeBlockFlags::STRICT;
let class_name = class
.name()
.name
.map_or(Sym::EMPTY_STRING, Identifier::sym)
.to_js_string(self.interner());
let old_lex_env = if class.has_binding_identifier() {
let old_lex_env = if class.has_binding_identifier {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
@ -63,7 +101,7 @@ impl ByteCompiler<'_> {
// Function environment
let _ = compiler.push_compile_environment(true);
if let Some(expr) = class.constructor() {
if let Some(expr) = &class.constructor {
compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone();
@ -78,7 +116,7 @@ impl ByteCompiler<'_> {
compiler.compile_statement_list(expr.body().statements(), false, false);
compiler.emit_opcode(Opcode::PushUndefined);
} else if class.super_ref().is_some() {
} else if class.super_ref.is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
compiler.emit_opcode(Opcode::BindThisValue);
} else {
@ -89,7 +127,7 @@ impl ByteCompiler<'_> {
// 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived.
compiler.code_block_flags.set(
CodeBlockFlags::IS_DERIVED_CONSTRUCTOR,
class.super_ref().is_some(),
class.super_ref.is_some(),
);
let code = Gc::new(compiler.finish());
@ -97,7 +135,7 @@ impl ByteCompiler<'_> {
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::Dup);
if let Some(node) = class.super_ref() {
if let Some(node) = &class.super_ref {
self.compile_expr(node, true);
self.emit_opcode(Opcode::PushClassPrototype);
} else {
@ -108,11 +146,16 @@ impl ByteCompiler<'_> {
let count_label = self.emit_opcode_with_operand(Opcode::PushPrivateEnvironment);
let mut count = 0;
for element in class.elements() {
for element in class.elements {
match element {
ClassElement::PrivateMethodDefinition(name, _)
| ClassElement::PrivateStaticMethodDefinition(name, _)
| ClassElement::PrivateFieldDefinition(name, _)
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PrivateName(name) = m.name() {
count += 1;
let index = self.get_or_insert_private_name(*name);
self.emit_u32(index);
}
}
ClassElement::PrivateFieldDefinition(name, _)
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
count += 1;
let index = self.get_or_insert_private_name(*name);
@ -131,144 +174,87 @@ impl ByteCompiler<'_> {
self.emit_binding(BindingOpcode::InitLexical, class_name.clone());
}
// TODO: set function name for getter and setters
for element in class.elements() {
for element in class.elements {
match element {
ClassElement::StaticMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticGetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticGetterByValue);
}
},
MethodDefinition::Set(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticSetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticSetterByValue);
}
},
MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::Async(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
ClassElement::MethodDefinition(m) => {
if !m.is_static() && !m.is_private() {
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Dup);
} else {
self.emit_opcode(Opcode::Dup);
}
}
ClassElement::PrivateStaticMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateGetter, index);
}
MethodDefinition::Set(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateSetter, index);
}
MethodDefinition::Ordinary(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
}
MethodDefinition::Async(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
match m.name() {
ClassElementName::PropertyName(PropertyName::Literal(name)) => {
self.method(m.into());
let index = self.get_or_insert_name((*name).into());
let opcode = match (m.is_static(), m.kind()) {
(true, MethodDefinitionKind::Get) => {
Opcode::DefineClassStaticGetterByName
}
(true, MethodDefinitionKind::Set) => {
Opcode::DefineClassStaticSetterByName
}
(true, _) => Opcode::DefineClassStaticMethodByName,
(false, MethodDefinitionKind::Get) => {
Opcode::DefineClassGetterByName
}
(false, MethodDefinitionKind::Set) => {
Opcode::DefineClassSetterByName
}
(false, _) => Opcode::DefineClassMethodByName,
};
self.emit_with_varying_operand(opcode, index);
}
MethodDefinition::Generator(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
ClassElementName::PropertyName(PropertyName::Computed(name)) => {
self.compile_expr(name, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(m.into());
let opcode = match (m.is_static(), m.kind()) {
(true, MethodDefinitionKind::Get) => {
Opcode::DefineClassStaticGetterByValue
}
(true, MethodDefinitionKind::Set) => {
Opcode::DefineClassStaticSetterByValue
}
(true, _) => Opcode::DefineClassStaticMethodByValue,
(false, MethodDefinitionKind::Get) => {
Opcode::DefineClassGetterByValue
}
(false, MethodDefinitionKind::Set) => {
Opcode::DefineClassSetterByValue
}
(false, _) => Opcode::DefineClassMethodByValue,
};
self.emit_opcode(opcode);
}
MethodDefinition::AsyncGenerator(expr) => {
self.method(expr.into());
ClassElementName::PrivateName(name) => {
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
let opcode = match (m.is_static(), m.kind()) {
(true, MethodDefinitionKind::Get) => Opcode::SetPrivateGetter,
(true, MethodDefinitionKind::Set) => Opcode::SetPrivateSetter,
(true, _) => Opcode::SetPrivateMethod,
(false, MethodDefinitionKind::Get) => {
Opcode::PushClassPrivateGetter
}
(false, MethodDefinitionKind::Set) => {
Opcode::PushClassPrivateSetter
}
(false, _) => {
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateRight, &[Operand::U8(4)]);
Opcode::PushClassPrivateMethod
}
};
self.method(m.into());
self.emit_with_varying_operand(opcode, index);
}
}
if !m.is_static() && !m.is_private() {
self.emit_opcode(Opcode::Swap);
}
}
ClassElement::FieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
@ -294,11 +280,13 @@ impl ByteCompiler<'_> {
// Function environment
let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field {
let is_anonymous_function = if let Some(node) = field {
field_compiler.compile_expr(node, true);
node.is_anonymous_function_definition()
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
false
};
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
@ -306,7 +294,10 @@ impl ByteCompiler<'_> {
let code = Gc::new(field_compiler.finish());
let index = self.push_function_to_constants(code);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::PushClassField);
self.emit(
Opcode::PushClassField,
&[Operand::Bool(is_anonymous_function)],
);
}
ClassElement::PrivateFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
@ -360,11 +351,13 @@ impl ByteCompiler<'_> {
self.in_with,
);
let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field {
let is_anonymous_function = if let Some(node) = field {
field_compiler.compile_expr(node, true);
node.is_anonymous_function_definition()
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
false
};
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
@ -372,7 +365,11 @@ impl ByteCompiler<'_> {
let code = field_compiler.finish();
let code = Gc::new(code);
static_elements.push(StaticElement::StaticField((code, name_index)));
static_elements.push(StaticElement::StaticField((
code,
name_index,
is_anonymous_function,
)));
}
ClassElement::PrivateStaticFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
@ -409,156 +406,6 @@ impl ByteCompiler<'_> {
let code = Gc::new(compiler.finish());
static_elements.push(StaticElement::StaticBlock(code));
}
ClassElement::PrivateMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateGetter, index);
}
MethodDefinition::Set(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateSetter, index);
}
MethodDefinition::Ordinary(expr) => {
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateRight, &[Operand::U8(4)]);
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::Async(expr) => {
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateRight, &[Operand::U8(4)]);
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::Generator(expr) => {
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateRight, &[Operand::U8(4)]);
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::AsyncGenerator(expr) => {
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateRight, &[Operand::U8(4)]);
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
}
}
ClassElement::MethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassGetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassGetterByValue);
}
},
MethodDefinition::Set(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassSetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassSetterByValue);
}
},
MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::Async(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
}
self.emit_opcode(Opcode::Swap);
}
}
}
@ -572,7 +419,7 @@ impl ByteCompiler<'_> {
self.emit_with_varying_operand(Opcode::Call, 0);
self.emit_opcode(Opcode::Pop);
}
StaticElement::StaticField((code, name_index)) => {
StaticElement::StaticField((code, name_index, is_anonymous_function)) => {
self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::Dup);
let index = self.push_function_to_constants(code);
@ -583,7 +430,16 @@ impl ByteCompiler<'_> {
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, name_index);
} else {
self.emit(Opcode::RotateLeft, &[Operand::U8(5)]);
self.emit_opcode(Opcode::Swap);
if is_anonymous_function {
self.emit_opcode(Opcode::Dup);
self.emit(Opcode::RotateLeft, &[Operand::U8(3)]);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::ToPropertyKey);
self.emit_opcode(Opcode::Swap);
self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]);
} else {
self.emit_opcode(Opcode::Swap);
}
self.emit_opcode(Opcode::DefineOwnPropertyByValue);
}
}

122
core/engine/src/bytecompiler/declarations.rs

@ -81,19 +81,15 @@ pub(crate) fn global_declaration_instantiation_context(
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = match declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// SKIP: 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).
@ -255,19 +251,15 @@ pub(crate) fn eval_declaration_instantiation_context(
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = match &declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// SKIP: 1. If varEnv is a Global Environment Record, then
@ -448,19 +440,15 @@ impl ByteCompiler<'_> {
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = match declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).
@ -536,7 +524,7 @@ impl ByteCompiler<'_> {
// 1. Perform ? env.CreateMutableBinding(dn, false).
if let StatementListItem::Declaration(declaration) = statement {
match declaration {
Declaration::Class(class) => {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class) {
let name = name.to_js_string(self.interner());
env.create_mutable_binding(name, false);
@ -563,23 +551,20 @@ impl ByteCompiler<'_> {
for function in functions_to_initialize {
// a. Let fn be the sole element of the BoundNames of f.
let (name, generator, r#async, parameters, body) = match &function {
VarScopedDeclaration::Function(f) => {
VarScopedDeclaration::FunctionDeclaration(f) => {
(f.name(), false, false, f.parameters(), f.body())
}
VarScopedDeclaration::Generator(f) => {
VarScopedDeclaration::GeneratorDeclaration(f) => {
(f.name(), true, false, f.parameters(), f.body())
}
VarScopedDeclaration::AsyncFunction(f) => {
VarScopedDeclaration::AsyncFunctionDeclaration(f) => {
(f.name(), false, true, f.parameters(), f.body())
}
VarScopedDeclaration::AsyncGenerator(f) => {
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => {
(f.name(), true, true, f.parameters(), f.body())
}
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
let name = name.expect("function declaration must have a name");
let code = FunctionCompiler::new()
.name(name.sym().to_js_string(self.interner()))
@ -684,16 +669,16 @@ impl ByteCompiler<'_> {
// TODO: Support B.3.2.6.
for d in declarations {
match d {
LexicallyScopedDeclaration::Function(function) => {
LexicallyScopedDeclaration::FunctionDeclaration(function) => {
self.function_with_binding(function.into(), NodeKind::Declaration, false);
}
LexicallyScopedDeclaration::Generator(function) => {
LexicallyScopedDeclaration::GeneratorDeclaration(function) => {
self.function_with_binding(function.into(), NodeKind::Declaration, false);
}
LexicallyScopedDeclaration::AsyncFunction(function) => {
LexicallyScopedDeclaration::AsyncFunctionDeclaration(function) => {
self.function_with_binding(function.into(), NodeKind::Declaration, false);
}
LexicallyScopedDeclaration::AsyncGenerator(function) => {
LexicallyScopedDeclaration::AsyncGeneratorDeclaration(function) => {
self.function_with_binding(function.into(), NodeKind::Declaration, false);
}
_ => {}
@ -795,19 +780,14 @@ impl ByteCompiler<'_> {
// a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
// a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = match &declaration {
VarScopedDeclaration::Function(f) => f.name(),
VarScopedDeclaration::Generator(f) => f.name(),
VarScopedDeclaration::AsyncFunction(f) => f.name(),
VarScopedDeclaration::AsyncGenerator(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
VarScopedDeclaration::FunctionDeclaration(f) => f.name(),
VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
// a.iii. Let fn be the sole element of the BoundNames of d.
let name = name.expect("function declaration must have a name");
// a.iv. If declaredFunctionNames does not contain fn, then
if !declared_function_names.contains(&name) {
// 1. If varEnv is a Global Environment Record, then
@ -906,7 +886,7 @@ impl ByteCompiler<'_> {
// 1. Perform ? lexEnv.CreateMutableBinding(dn, false).
if let StatementListItem::Declaration(declaration) = statement {
match declaration {
Declaration::Class(class) => {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class) {
let name = name.to_js_string(self.interner());
lex_env.create_mutable_binding(name, false);
@ -933,23 +913,23 @@ impl ByteCompiler<'_> {
for function in functions_to_initialize {
// a. Let fn be the sole element of the BoundNames of f.
let (name, generator, r#async, parameters, body) = match &function {
VarScopedDeclaration::Function(f) => {
VarScopedDeclaration::FunctionDeclaration(f) => {
(f.name(), false, false, f.parameters(), f.body())
}
VarScopedDeclaration::Generator(f) => {
VarScopedDeclaration::GeneratorDeclaration(f) => {
(f.name(), true, false, f.parameters(), f.body())
}
VarScopedDeclaration::AsyncFunction(f) => {
VarScopedDeclaration::AsyncFunctionDeclaration(f) => {
(f.name(), false, true, f.parameters(), f.body())
}
VarScopedDeclaration::AsyncGenerator(f) => {
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => {
(f.name(), true, true, f.parameters(), f.body())
}
VarScopedDeclaration::VariableDeclaration(_) => {
continue;
}
};
let name = name.expect("function declaration must have a name");
let code = FunctionCompiler::new()
.name(name.sym().to_js_string(self.interner()))
.generator(generator)
@ -1094,19 +1074,19 @@ impl ByteCompiler<'_> {
for declaration in var_declarations.iter().rev() {
// a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
// a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
let function = match declaration {
VarScopedDeclaration::Function(f) => FunctionSpec::from(f),
VarScopedDeclaration::Generator(f) => FunctionSpec::from(f),
VarScopedDeclaration::AsyncFunction(f) => FunctionSpec::from(f),
VarScopedDeclaration::AsyncGenerator(f) => FunctionSpec::from(f),
// a.ii. Let fn be the sole element of the BoundNames of d.
let (name, function) = match declaration {
VarScopedDeclaration::FunctionDeclaration(f) => (f.name(), FunctionSpec::from(f)),
VarScopedDeclaration::GeneratorDeclaration(f) => (f.name(), FunctionSpec::from(f)),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => {
(f.name(), FunctionSpec::from(f))
}
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => {
(f.name(), FunctionSpec::from(f))
}
VarScopedDeclaration::VariableDeclaration(_) => continue,
};
// a.ii. Let fn be the sole element of the BoundNames of d.
let name = function
.name
.expect("function declaration must have a name");
// a.iii. If functionNames does not contain fn, then
if !function_names.contains(&name) {
// 1. Insert fn as the first element of functionNames.
@ -1427,7 +1407,7 @@ impl ByteCompiler<'_> {
for statement in &**body.statements() {
if let StatementListItem::Declaration(declaration) = statement {
match declaration {
Declaration::Class(class) => {
Declaration::ClassDeclaration(class) => {
for name in bound_names(class) {
let name = name.to_js_string(self.interner());
lex_env.create_mutable_binding(name, false);

12
core/engine/src/bytecompiler/expression/mod.rs

@ -4,6 +4,8 @@ mod object_literal;
mod unary;
mod update;
use std::ops::Deref;
use super::{Access, Callable, NodeKind, Operand, ToJsString};
use crate::{
bytecompiler::{ByteCompiler, Literal},
@ -133,7 +135,7 @@ impl ByteCompiler<'_> {
self.access_get(Access::This, use_expr);
}
Expression::Spread(spread) => self.compile_expr(spread.target(), true),
Expression::Function(function) => {
Expression::FunctionExpression(function) => {
self.function_with_binding(function.into(), NodeKind::Expression, use_expr);
}
Expression::ArrowFunction(function) => {
@ -142,13 +144,13 @@ impl ByteCompiler<'_> {
Expression::AsyncArrowFunction(function) => {
self.function_with_binding(function.into(), NodeKind::Expression, use_expr);
}
Expression::Generator(function) => {
Expression::GeneratorExpression(function) => {
self.function_with_binding(function.into(), NodeKind::Expression, use_expr);
}
Expression::AsyncFunction(function) => {
Expression::AsyncFunctionExpression(function) => {
self.function_with_binding(function.into(), NodeKind::Expression, use_expr);
}
Expression::AsyncGenerator(function) => {
Expression::AsyncGeneratorExpression(function) => {
self.function_with_binding(function.into(), NodeKind::Expression, use_expr);
}
Expression::Call(call) => self.call(Callable::Call(call), use_expr),
@ -289,7 +291,7 @@ impl ByteCompiler<'_> {
self.emit_with_varying_operand(Opcode::Call, template.exprs().len() as u32 + 1);
}
Expression::Class(class) => self.class(class, true),
Expression::ClassExpression(class) => self.class(class.deref().into(), true),
Expression::SuperCall(super_call) => {
self.emit_opcode(Opcode::SuperCallPrepare);

119
core/engine/src/bytecompiler/expression/object_literal.rs

@ -3,8 +3,8 @@ use crate::{
vm::Opcode,
};
use boa_ast::{
expression::literal::ObjectLiteral,
property::{MethodDefinition, PropertyDefinition, PropertyName},
expression::literal::{ObjectLiteral, PropertyDefinition},
property::{MethodDefinitionKind, PropertyName},
Expression,
};
use boa_interner::Sym;
@ -43,110 +43,29 @@ impl ByteCompiler<'_> {
self.emit_opcode(Opcode::DefineOwnPropertyByValue);
}
},
PropertyDefinition::MethodDefinition(name, kind) => match kind {
MethodDefinition::Get(expr) => match name {
PropertyDefinition::MethodDefinition(m) => {
let kind = match m.kind() {
MethodDefinitionKind::Get => MethodKind::Get,
MethodDefinitionKind::Set => MethodKind::Set,
_ => MethodKind::Ordinary,
};
match m.name() {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Get);
let opcode = match kind {
MethodKind::Get => Opcode::SetPropertyGetterByName,
MethodKind::Set => Opcode::SetPropertySetterByName,
MethodKind::Ordinary => Opcode::DefineOwnPropertyByName,
};
self.object_method((m, *name).into(), kind);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::SetPropertyGetterByName, index);
self.emit_with_varying_operand(opcode, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Get,
);
self.compile_object_literal_computed_method(name_node, m.into(), kind);
}
},
MethodDefinition::Set(expr) => match name {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Set);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::SetPropertySetterByName, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Set,
);
}
},
MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Ordinary);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Ordinary,
);
}
},
MethodDefinition::Async(expr) => match name {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Ordinary);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Ordinary,
);
}
},
MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Ordinary);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Ordinary,
);
}
},
MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(name) => {
let mut method: FunctionSpec<'_> = expr.into();
method.name = Some((*name).into());
self.object_method(method, MethodKind::Ordinary);
self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
}
PropertyName::Computed(name_node) => {
self.compile_object_literal_computed_method(
name_node,
expr.into(),
MethodKind::Ordinary,
);
}
},
},
}
}
PropertyDefinition::SpreadObject(expr) => {
self.compile_expr(expr, true);
self.emit_opcode(Opcode::Swap);

140
core/engine/src/bytecompiler/mod.rs

@ -27,20 +27,25 @@ use boa_ast::{
declaration::{Binding, LexicalDeclaration, VarDeclaration},
expression::{
access::{PropertyAccess, PropertyAccessField},
literal::ObjectMethodDefinition,
operator::{assign::AssignTarget, update::UpdateTarget},
Call, Identifier, New, Optional, OptionalOperationKind,
},
function::{
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class,
FormalParameterList, Function, FunctionBody, Generator, PrivateName,
ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassMethodDefinition,
FormalParameterList, FunctionBody, FunctionDeclaration, FunctionExpression,
GeneratorDeclaration, GeneratorExpression, PrivateName,
},
operations::returns_value,
pattern::Pattern,
property::MethodDefinitionKind,
Declaration, Expression, Statement, StatementList, StatementListItem,
};
use boa_gc::Gc;
use boa_interner::{Interner, Sym};
use boa_macros::js_str;
use class::ClassSpec;
use rustc_hash::FxHashMap;
use thin_vec::ThinVec;
@ -116,8 +121,56 @@ pub(crate) struct FunctionSpec<'a> {
pub(crate) has_binding_identifier: bool,
}
impl<'a> From<&'a Function> for FunctionSpec<'a> {
fn from(function: &'a Function) -> Self {
impl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> {
fn from(function: &'a FunctionDeclaration) -> Self {
FunctionSpec {
kind: FunctionKind::Ordinary,
name: Some(function.name()),
parameters: function.parameters(),
body: function.body(),
has_binding_identifier: true,
}
}
}
impl<'a> From<&'a GeneratorDeclaration> for FunctionSpec<'a> {
fn from(function: &'a GeneratorDeclaration) -> Self {
FunctionSpec {
kind: FunctionKind::Generator,
name: Some(function.name()),
parameters: function.parameters(),
body: function.body(),
has_binding_identifier: true,
}
}
}
impl<'a> From<&'a AsyncFunctionDeclaration> for FunctionSpec<'a> {
fn from(function: &'a AsyncFunctionDeclaration) -> Self {
FunctionSpec {
kind: FunctionKind::Async,
name: Some(function.name()),
parameters: function.parameters(),
body: function.body(),
has_binding_identifier: true,
}
}
}
impl<'a> From<&'a AsyncGeneratorDeclaration> for FunctionSpec<'a> {
fn from(function: &'a AsyncGeneratorDeclaration) -> Self {
FunctionSpec {
kind: FunctionKind::AsyncGenerator,
name: Some(function.name()),
parameters: function.parameters(),
body: function.body(),
has_binding_identifier: true,
}
}
}
impl<'a> From<&'a FunctionExpression> for FunctionSpec<'a> {
fn from(function: &'a FunctionExpression) -> Self {
FunctionSpec {
kind: FunctionKind::Ordinary,
name: function.name(),
@ -152,8 +205,8 @@ impl<'a> From<&'a AsyncArrowFunction> for FunctionSpec<'a> {
}
}
impl<'a> From<&'a AsyncFunction> for FunctionSpec<'a> {
fn from(function: &'a AsyncFunction) -> Self {
impl<'a> From<&'a AsyncFunctionExpression> for FunctionSpec<'a> {
fn from(function: &'a AsyncFunctionExpression) -> Self {
FunctionSpec {
kind: FunctionKind::Async,
name: function.name(),
@ -164,8 +217,8 @@ impl<'a> From<&'a AsyncFunction> for FunctionSpec<'a> {
}
}
impl<'a> From<&'a Generator> for FunctionSpec<'a> {
fn from(function: &'a Generator) -> Self {
impl<'a> From<&'a GeneratorExpression> for FunctionSpec<'a> {
fn from(function: &'a GeneratorExpression) -> Self {
FunctionSpec {
kind: FunctionKind::Generator,
name: function.name(),
@ -176,8 +229,8 @@ impl<'a> From<&'a Generator> for FunctionSpec<'a> {
}
}
impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> {
fn from(function: &'a AsyncGenerator) -> Self {
impl<'a> From<&'a AsyncGeneratorExpression> for FunctionSpec<'a> {
fn from(function: &'a AsyncGeneratorExpression) -> Self {
FunctionSpec {
kind: FunctionKind::AsyncGenerator,
name: function.name(),
@ -188,6 +241,63 @@ impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> {
}
}
impl<'a> From<&'a ClassMethodDefinition> for FunctionSpec<'a> {
fn from(method: &'a ClassMethodDefinition) -> Self {
let kind = match method.kind() {
MethodDefinitionKind::Generator => FunctionKind::Generator,
MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,
MethodDefinitionKind::Async => FunctionKind::Async,
_ => FunctionKind::Ordinary,
};
FunctionSpec {
kind,
name: None,
parameters: method.parameters(),
body: method.body(),
has_binding_identifier: false,
}
}
}
impl<'a> From<&'a ObjectMethodDefinition> for FunctionSpec<'a> {
fn from(method: &'a ObjectMethodDefinition) -> Self {
let kind = match method.kind() {
MethodDefinitionKind::Generator => FunctionKind::Generator,
MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,
MethodDefinitionKind::Async => FunctionKind::Async,
_ => FunctionKind::Ordinary,
};
FunctionSpec {
kind,
name: None,
parameters: method.parameters(),
body: method.body(),
has_binding_identifier: false,
}
}
}
impl<'a> From<(&'a ObjectMethodDefinition, Sym)> for FunctionSpec<'a> {
fn from(method: (&'a ObjectMethodDefinition, Sym)) -> Self {
let kind = match method.0.kind() {
MethodDefinitionKind::Generator => FunctionKind::Generator,
MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,
MethodDefinitionKind::Async => FunctionKind::Async,
_ => FunctionKind::Ordinary,
};
FunctionSpec {
kind,
name: Some(Identifier::new(method.1)),
parameters: method.0.parameters(),
body: method.0.body(),
has_binding_identifier: true,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum MethodKind {
Get,
@ -1267,10 +1377,8 @@ impl<'ctx> ByteCompiler<'ctx> {
pub fn compile_decl(&mut self, decl: &Declaration, block: bool) {
match decl {
#[cfg(feature = "annex-b")]
Declaration::Function(function) if block => {
let name = function
.name()
.expect("function declaration must have name");
Declaration::FunctionDeclaration(function) if block => {
let name = function.name();
if self.annex_b_function_names.contains(&name) {
let name = name.to_js_string(self.interner());
let binding = self
@ -1297,7 +1405,7 @@ impl<'ctx> ByteCompiler<'ctx> {
}
}
}
Declaration::Class(class) => self.class(class, false),
Declaration::ClassDeclaration(class) => self.class(class.into(), false),
Declaration::Lexical(lexical) => self.compile_lexical_decl(lexical),
_ => {}
}
@ -1622,7 +1730,7 @@ impl<'ctx> ByteCompiler<'ctx> {
self.compile_declaration_pattern_impl(pattern, def);
}
fn class(&mut self, class: &Class, expression: bool) {
fn class(&mut self, class: ClassSpec<'_>, expression: bool) {
self.compile_class(class, expression);
}
}

18
core/engine/src/bytecompiler/module.rs

@ -34,23 +34,15 @@ impl ByteCompiler<'_> {
// export NamedExports ;
// 1. Return empty.
}
ExportDeclaration::DefaultFunction(_)
| ExportDeclaration::DefaultGenerator(_)
| ExportDeclaration::DefaultAsyncFunction(_)
| ExportDeclaration::DefaultAsyncGenerator(_) => {
ExportDeclaration::DefaultFunctionDeclaration(_)
| ExportDeclaration::DefaultGeneratorDeclaration(_)
| ExportDeclaration::DefaultAsyncFunctionDeclaration(_)
| ExportDeclaration::DefaultAsyncGeneratorDeclaration(_) => {
// Already instantiated in `initialize_environment`.
}
ExportDeclaration::VarStatement(var) => self.compile_var_decl(var),
ExportDeclaration::Declaration(decl) => self.compile_decl(decl, false),
ExportDeclaration::DefaultClassDeclaration(cl) => {
self.class(cl, cl.name().is_none());
if cl.name().is_none() {
self.emit_binding(
BindingOpcode::InitLexical,
Sym::DEFAULT_EXPORT.to_js_string(self.interner()),
);
}
}
ExportDeclaration::DefaultClassDeclaration(cl) => self.class(cl.into(), false),
ExportDeclaration::DefaultAssignmentExpression(expr) => {
let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner());
self.lexical_environment

2
core/engine/src/bytecompiler/statement/labelled.rs

@ -29,7 +29,7 @@ impl ByteCompiler<'_> {
}
stmt => self.compile_stmt(stmt, use_expr, true),
},
LabelledItem::Function(f) => {
LabelledItem::FunctionDeclaration(f) => {
self.function_with_binding(f.into(), NodeKind::Declaration, false);
}
}

10
core/engine/src/module/source.rs

@ -1549,31 +1549,31 @@ impl SourceTextModule {
//
// deferred to below.
let (mut spec, locator): (FunctionSpec<'_>, _) = match declaration {
LexicallyScopedDeclaration::Function(f) => {
LexicallyScopedDeclaration::FunctionDeclaration(f) => {
let name = bound_names(f)[0].to_js_string(compiler.interner());
let locator = env.create_mutable_binding(name, false);
(f.into(), locator)
}
LexicallyScopedDeclaration::Generator(g) => {
LexicallyScopedDeclaration::GeneratorDeclaration(g) => {
let name = bound_names(g)[0].to_js_string(compiler.interner());
let locator = env.create_mutable_binding(name, false);
(g.into(), locator)
}
LexicallyScopedDeclaration::AsyncFunction(af) => {
LexicallyScopedDeclaration::AsyncFunctionDeclaration(af) => {
let name = bound_names(af)[0].to_js_string(compiler.interner());
let locator = env.create_mutable_binding(name, false);
(af.into(), locator)
}
LexicallyScopedDeclaration::AsyncGenerator(ag) => {
LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag) => {
let name = bound_names(ag)[0].to_js_string(compiler.interner());
let locator = env.create_mutable_binding(name, false);
(ag.into(), locator)
}
LexicallyScopedDeclaration::Class(class) => {
LexicallyScopedDeclaration::ClassDeclaration(class) => {
for name in bound_names(class) {
let name = name.to_js_string(compiler.interner());
env.create_mutable_binding(name, false);

17
core/engine/src/object/operations.rs

@ -1,6 +1,6 @@
use crate::{
builtins::{
function::{BoundFunction, ClassFieldDefinition, OrdinaryFunction},
function::{set_function_name, BoundFunction, ClassFieldDefinition, OrdinaryFunction},
Array, Proxy,
},
context::intrinsics::{StandardConstructor, StandardConstructors},
@ -1077,7 +1077,7 @@ impl JsObject {
) -> JsResult<()> {
// 2. Let initializer be fieldRecord.[[Initializer]].
let initializer = match field_record {
ClassFieldDefinition::Public(_, function)
ClassFieldDefinition::Public(_, function, _)
| ClassFieldDefinition::Private(_, function) => function,
};
@ -1095,7 +1095,18 @@ impl JsObject {
}
// 1. Let fieldName be fieldRecord.[[Name]].
// 6. Else,
ClassFieldDefinition::Public(field_name, _) => {
ClassFieldDefinition::Public(field_name, _, function_name) => {
if let Some(function_name) = function_name {
set_function_name(
init_value
.as_object()
.expect("init value must be a function object"),
function_name,
None,
context,
);
}
// a. Assert: IsPropertyKey(fieldName) is true.
// b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).
self.create_data_property_or_throw(field_name.clone(), init_value, context)?;

38
core/engine/src/tests/class.rs

@ -0,0 +1,38 @@
use crate::{run_test_actions, TestAction};
use boa_macros::js_str;
use indoc::indoc;
#[test]
fn class_field_initializer_name_static() {
run_test_actions([
TestAction::run(indoc! {r#"
class C {
static a = function() {};
static ["b"] = function() {};
static #c = function() {};
static c = this.#c
}
"#}),
TestAction::assert_eq("C.a.name", js_str!("a")),
TestAction::assert_eq("C.b.name", js_str!("b")),
TestAction::assert_eq("C.c.name", js_str!("#c")),
]);
}
#[test]
fn class_field_initializer_name() {
run_test_actions([
TestAction::run(indoc! {r#"
class C {
a = function() {};
["b"] = function() {};
#c = function() {};
c = this.#c
}
let c = new C();
"#}),
TestAction::assert_eq("c.a.name", js_str!("a")),
TestAction::assert_eq("c.b.name", js_str!("b")),
TestAction::assert_eq("c.c.name", js_str!("#c")),
]);
}

1
core/engine/src/tests/mod.rs

@ -4,6 +4,7 @@ use boa_macros::js_str;
use indoc::indoc;
mod async_generator;
mod class;
mod control_flow;
mod env;
mod function;

6
core/engine/src/vm/code_block.rs

@ -544,6 +544,11 @@ impl CodeBlock {
.to_std_string_escaped();
format!("name: {name}, configurable: {configurable}")
}
Instruction::PushClassField {
is_anonymous_function,
} => {
format!("is_anonymous_function: {is_anonymous_function}")
}
Instruction::Pop
| Instruction::Dup
| Instruction::Swap
@ -645,7 +650,6 @@ impl CodeBlock {
| Instruction::GeneratorYield
| Instruction::AsyncGeneratorYield
| Instruction::GeneratorNext
| Instruction::PushClassField
| Instruction::SuperCallDerived
| Instruction::Await
| Instruction::NewTarget

2
core/engine/src/vm/flowgraph/mod.rs

@ -429,7 +429,7 @@ impl CodeBlock {
| Instruction::CreatePromiseCapability
| Instruction::CompletePromiseCapability
| Instruction::GeneratorNext
| Instruction::PushClassField
| Instruction::PushClassField { .. }
| Instruction::SuperCallDerived
| Instruction::Await
| Instruction::NewTarget

4
core/engine/src/vm/opcode/mod.rs

@ -1420,10 +1420,10 @@ generate_opcodes! {
/// Push a field to a class.
///
/// Operands:
/// Operands: is_anonymous_function: `bool`
///
/// Stack: class, field_name, field_function **=>**
PushClassField,
PushClassField { is_anonymous_function: bool },
/// Push a private field to the class.
///

8
core/engine/src/vm/opcode/push/class/field.rs

@ -18,6 +18,7 @@ impl Operation for PushClassField {
const COST: u8 = 6;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let is_annonymus_function = context.vm.read::<u8>() != 0;
let field_function_value = context.vm.pop();
let field_name_value = context.vm.pop();
let class_value = context.vm.pop();
@ -39,8 +40,13 @@ impl Operation for PushClassField {
.downcast_mut::<OrdinaryFunction>()
.expect("class must be function object")
.push_field(
field_name_key,
field_name_key.clone(),
JsFunction::from_object_unchecked(field_function_object.clone()),
if is_annonymus_function {
Some(field_name_key)
} else {
None
},
);
Ok(CompletionType::Normal)
}

16
core/parser/src/parser/expression/assignment/arrow_function.rs

@ -23,7 +23,6 @@ use ast::operations::{bound_names, lexically_declared_names};
use boa_ast::{
self as ast,
declaration::Variable,
expression::Identifier,
function::{FormalParameter, FormalParameterList},
operations::{contains, ContainsSymbol},
statement::Return,
@ -42,7 +41,6 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct ArrowFunction {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -50,20 +48,13 @@ pub(in crate::parser) struct ArrowFunction {
impl ArrowFunction {
/// Creates a new `ArrowFunction` parser.
pub(in crate::parser) fn new<N, I, Y, A>(
name: N,
allow_in: I,
allow_yield: Y,
allow_await: A,
) -> Self
pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -162,7 +153,7 @@ where
interner,
)?;
Ok(ast::function::ArrowFunction::new(self.name, params, body))
Ok(ast::function::ArrowFunction::new(None, params, body))
}
}
@ -241,7 +232,6 @@ where
type Output = Expression;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
AssignmentExpression::new(None, self.allow_in, false, self.allow_await)
.parse(cursor, interner)
AssignmentExpression::new(self.allow_in, false, self.allow_await).parse(cursor, interner)
}
}

10
core/parser/src/parser/expression/assignment/async_arrow_function.rs

@ -25,7 +25,6 @@ use ast::{
use boa_ast::{
self as ast,
declaration::Variable,
expression::Identifier,
function::{FormalParameter, FormalParameterList},
statement::Return,
Punctuator, StatementList,
@ -43,21 +42,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct AsyncArrowFunction {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
}
impl AsyncArrowFunction {
/// Creates a new `AsyncArrowFunction` parser.
pub(in crate::parser) fn new<N, I, Y>(name: N, allow_in: I, allow_yield: Y) -> Self
pub(in crate::parser) fn new<I, Y>(allow_in: I, allow_yield: Y) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
}
@ -148,9 +144,7 @@ where
interner,
)?;
Ok(ast::function::AsyncArrowFunction::new(
self.name, params, body,
))
Ok(ast::function::AsyncArrowFunction::new(None, params, body))
}
}

32
core/parser/src/parser/expression/assignment/conditional.rs

@ -15,10 +15,7 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{
expression::{operator::Conditional, Identifier},
Expression, Punctuator,
};
use boa_ast::{expression::operator::Conditional, Expression, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
@ -32,7 +29,6 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
#[derive(Debug, Clone, Copy)]
pub(in crate::parser::expression) struct ConditionalExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -40,20 +36,17 @@ pub(in crate::parser::expression) struct ConditionalExpression {
impl ConditionalExpression {
/// Creates a new `ConditionalExpression` parser.
pub(in crate::parser::expression) fn new<N, I, Y, A>(
name: N,
pub(in crate::parser::expression) fn new<I, Y, A>(
allow_in: I,
allow_yield: Y,
allow_await: A,
) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -69,29 +62,20 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("ConditionalExpression", "Parsing");
let lhs = ShortCircuitExpression::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let lhs = ShortCircuitExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if let Some(tok) = cursor.peek(0, interner)? {
if tok.kind() == &TokenKind::Punctuator(Punctuator::Question) {
cursor.advance(interner);
let then_clause =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::Colon, "conditional expression", interner)?;
let else_clause = AssignmentExpression::new(
None,
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let else_clause =
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(Conditional::new(lhs, then_clause, else_clause).into());
}
}

22
core/parser/src/parser/expression/assignment/exponentiation.rs

@ -16,10 +16,7 @@ use crate::{
source::ReadChar,
};
use boa_ast::{
expression::{
operator::{binary::ArithmeticOp, Binary},
Identifier,
},
expression::operator::{binary::ArithmeticOp, Binary},
Expression, Keyword, Punctuator,
};
use boa_interner::Interner;
@ -35,25 +32,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-ExponentiationExpression
#[derive(Debug, Clone, Copy)]
pub(in crate::parser::expression) struct ExponentiationExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ExponentiationExpression {
/// Creates a new `ExponentiationExpression` parser.
pub(in crate::parser::expression) fn new<N, Y, A>(
name: N,
allow_yield: Y,
allow_await: A,
) -> Self
pub(in crate::parser::expression) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -75,18 +65,18 @@ where
| TokenKind::Punctuator(
Punctuator::Add | Punctuator::Sub | Punctuator::Not | Punctuator::Neg,
) => {
return UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
return UnaryExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner);
}
TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => {
return UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
return UnaryExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner);
}
_ => {}
}
let lhs = UpdateExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let lhs =
UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
if let Some(tok) = cursor.peek(0, interner)? {
if tok.kind() == &TokenKind::Punctuator(Punctuator::Exp) {
cursor.advance(interner);

55
core/parser/src/parser/expression/assignment/mod.rs

@ -29,10 +29,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::{
operator::assign::{Assign, AssignOp, AssignTarget},
Identifier,
},
expression::operator::assign::{Assign, AssignOp, AssignTarget},
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol},
Expression, Keyword, Punctuator,
};
@ -61,7 +58,6 @@ pub(super) use exponentiation::ExponentiationExpression;
/// [lhs]: ../lhs_expression/struct.LeftHandSideExpression.html
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct AssignmentExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -69,20 +65,13 @@ pub(in crate::parser) struct AssignmentExpression {
impl AssignmentExpression {
/// Creates a new `AssignmentExpression` parser.
pub(in crate::parser) fn new<N, I, Y, A>(
name: N,
allow_in: I,
allow_yield: Y,
allow_await: A,
) -> Self
pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -96,7 +85,7 @@ where
{
type Output = Expression;
fn parse(mut self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Expression> {
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Expression> {
let _timer = Profiler::global().start_event("AssignmentExpression", "Parsing");
cursor.set_goal(InputElement::RegExp);
@ -123,7 +112,6 @@ where
{
if tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow) {
return ArrowFunction::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
@ -158,11 +146,9 @@ where
TokenKind::Punctuator(Punctuator::Arrow)
)))
{
return Ok(
AsyncArrowFunction::new(self.name, self.allow_in, self.allow_yield)
.parse(cursor, interner)?
.into(),
);
return Ok(AsyncArrowFunction::new(self.allow_in, self.allow_yield)
.parse(cursor, interner)?
.into());
}
}
_ => {}
@ -171,13 +157,8 @@ where
cursor.set_goal(InputElement::Div);
let position = cursor.peek(0, interner).or_abrupt()?.span().start();
let mut lhs = ConditionalExpression::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let mut lhs = ConditionalExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
// If the left hand side is a parameter list, we must parse an arrow function.
if let Expression::FormalParameterList(parameters) = lhs {
@ -237,7 +218,7 @@ where
interner,
)?;
return Ok(boa_ast::function::ArrowFunction::new(self.name, parameters, body).into());
return Ok(boa_ast::function::ArrowFunction::new(None, parameters, body).into());
}
// Review if we are trying to assign to an invalid left hand side expression.
@ -247,11 +228,17 @@ where
cursor.advance(interner);
cursor.set_goal(InputElement::RegExp);
let lhs_name = if let Expression::Identifier(ident) = lhs {
Some(ident)
} else {
None
};
if let Some(target) = AssignTarget::from_expression(&lhs, cursor.strict()) {
if let Expression::Identifier(ident) = lhs {
self.name = Some(ident);
let mut expr = self.parse(cursor, interner)?;
if let Some(ident) = lhs_name {
expr.set_anonymous_function_definition_name(&ident);
}
let expr = self.parse(cursor, interner)?;
lhs = Assign::new(AssignOp::Assign, target, expr).into();
} else {
return Err(Error::lex(LexError::Syntax(
@ -266,16 +253,16 @@ where
AssignTarget::from_expression_simple(&lhs, cursor.strict())
{
let assignop = p.as_assign_op().expect("assignop disappeared");
let mut rhs = self.parse(cursor, interner)?;
if assignop == AssignOp::BoolAnd
|| assignop == AssignOp::BoolOr
|| assignop == AssignOp::Coalesce
{
if let AssignTarget::Identifier(ident) = target {
self.name = Some(ident);
rhs.set_anonymous_function_definition_name(&ident);
}
}
let rhs = self.parse(cursor, interner)?;
lhs = Assign::new(assignop, target, rhs).into();
} else {
return Err(Error::lex(LexError::Syntax(

4
core/parser/src/parser/expression/assignment/yield.rs

@ -71,7 +71,7 @@ where
match token.kind() {
TokenKind::Punctuator(Punctuator::Mul) => {
cursor.advance(interner);
let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await)
let expr = AssignmentExpression::new(self.allow_in, true, self.allow_await)
.parse(cursor, interner)?;
Ok(Yield::new(Some(expr), true).into())
}
@ -110,7 +110,7 @@ where
| TokenKind::NumericLiteral(_)
| TokenKind::RegularExpressionLiteral(_, _)
| TokenKind::TemplateMiddle(_) => {
let expr = AssignmentExpression::new(None, self.allow_in, true, self.allow_await)
let expr = AssignmentExpression::new(self.allow_in, true, self.allow_await)
.parse(cursor, interner)?;
Ok(Yield::new(Some(expr), false).into())
}

2
core/parser/src/parser/expression/await_expr.rs

@ -53,7 +53,7 @@ where
"Await expression parsing",
interner,
)?;
let expr = UnaryExpression::new(None, self.allow_yield, true).parse(cursor, interner)?;
let expr = UnaryExpression::new(self.allow_yield, true).parse(cursor, interner)?;
Ok(expr.into())
}
}

4
core/parser/src/parser/expression/left_hand_side/arguments.rs

@ -99,14 +99,14 @@ where
if cursor.next_if(Punctuator::Spread, interner)?.is_some() {
args.push(
Spread::new(
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
.into(),
);
} else {
args.push(
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
);
}

2
core/parser/src/parser/expression/left_hand_side/call.rs

@ -166,7 +166,7 @@ where
}
TokenKind::Punctuator(Punctuator::OpenBracket) => {
cursor.advance(interner);
let idx = Expression::new(None, true, self.allow_yield, self.allow_await)
let idx = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "call expression", interner)?;
lhs =

13
core/parser/src/parser/expression/left_hand_side/member.rs

@ -24,7 +24,7 @@ use boa_ast::{
access::{
PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess, SuperPropertyAccess,
},
Call, Identifier, New,
Call, New,
},
Keyword, Punctuator,
};
@ -39,21 +39,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct MemberExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl MemberExpression {
/// Creates a new `MemberExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -195,7 +192,7 @@ where
ast::Expression::PropertyAccess(field.into())
}
TokenKind::Punctuator(Punctuator::OpenBracket) => {
let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
let expr = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "super property", interner)?;
ast::Expression::PropertyAccess(
@ -211,7 +208,7 @@ where
}
}
}
_ => PrimaryExpression::new(self.name, self.allow_yield, self.allow_await)
_ => PrimaryExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
};
@ -261,7 +258,7 @@ where
cursor
.next(interner)?
.expect("open bracket punctuator token disappeared"); // We move the parser forward.
let idx = Expression::new(None, true, self.allow_yield, self.allow_await)
let idx = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "member expression", interner)?;
lhs =

11
core/parser/src/parser/expression/left_hand_side/mod.rs

@ -34,7 +34,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::{Identifier, ImportCall, SuperCall},
expression::{ImportCall, SuperCall},
Expression, Keyword, Punctuator,
};
use boa_interner::Interner;
@ -50,21 +50,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct LeftHandSideExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl LeftHandSideExpression {
/// Creates a new `LeftHandSideExpression` parser.
pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -130,7 +127,7 @@ where
// `(`
cursor.advance(interner);
let arg = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
let arg = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(
@ -146,7 +143,7 @@ where
)
.parse(cursor, interner)?
} else {
let mut member = MemberExpression::new(self.name, self.allow_yield, self.allow_await)
let mut member = MemberExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if let Some(tok) = cursor.peek(0, interner)? {
if tok.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) {

2
core/parser/src/parser/expression/left_hand_side/optional/mod.rs

@ -143,7 +143,7 @@ where
cursor
.next(interner)?
.expect("open bracket punctuator token disappeared"); // We move the parser forward.
let idx = Expression::new(None, true, self.allow_yield, self.allow_await)
let idx = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "optional chain", interner)?;
OptionalOperationKind::SimplePropertyAccess {

2
core/parser/src/parser/expression/left_hand_side/template.rs

@ -67,7 +67,7 @@ where
raws.push(template_string.raw());
cookeds.push(template_string.cooked());
exprs.push(
Expression::new(None, true, self.allow_yield, self.allow_await)
Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
);
cursor.expect(

121
core/parser/src/parser/expression/mod.rs

@ -77,7 +77,7 @@ macro_rules! expression {
{
type Output = ast::Expression;
fn parse(mut self, cursor: &mut Cursor<R>, interner: &mut Interner)-> ParseResult<ast::Expression> {
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner)-> ParseResult<ast::Expression> {
let _timer = Profiler::global().start_event(stringify!($name), "Parsing");
if $goal.is_some() {
@ -85,7 +85,6 @@ macro_rules! expression {
}
let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?;
self.name = None;
while let Some(tok) = cursor.peek(0, interner)? {
match *tok.kind() {
TokenKind::Punctuator(op) if $( op == $op )||* => {
@ -116,7 +115,6 @@ macro_rules! expression {
/// [spec]: https://tc39.es/ecma262/#prod-Expression
#[derive(Debug, Clone, Copy)]
pub(super) struct Expression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -124,15 +122,13 @@ pub(super) struct Expression {
impl Expression {
/// Creates a new `Expression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -146,17 +142,11 @@ where
{
type Output = ast::Expression;
fn parse(
mut self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> ParseResult<Self::Output> {
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("Expression", "Parsing");
let mut lhs =
AssignmentExpression::new(self.name, self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
self.name = None;
let mut lhs = AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
while let Some(tok) = cursor.peek(0, interner)? {
match *tok.kind() {
TokenKind::Punctuator(Punctuator::Comma) => {
@ -180,7 +170,6 @@ where
.expect("Could not get binary operation."),
lhs,
AssignmentExpression::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
@ -207,7 +196,6 @@ where
/// [spec]: https://tc39.es/ecma262/#prod-ShortCircuitExpression
#[derive(Debug, Clone, Copy)]
struct ShortCircuitExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -223,15 +211,13 @@ enum PreviousExpr {
impl ShortCircuitExpression {
/// Creates a new `ShortCircuitExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -239,21 +225,18 @@ impl ShortCircuitExpression {
}
}
fn with_previous<N, I, Y, A>(
name: N,
fn with_previous<I, Y, A>(
allow_in: I,
allow_yield: Y,
allow_await: A,
previous: PreviousExpr,
) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -272,7 +255,7 @@ where
let _timer = Profiler::global().start_event("ShortCircuitExpression", "Parsing");
let mut current_node =
BitwiseORExpression::new(self.name, self.allow_in, self.allow_yield, self.allow_await)
BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let mut previous = self.previous;
@ -288,13 +271,9 @@ where
}
cursor.advance(interner);
previous = PreviousExpr::Logical;
let rhs = BitwiseORExpression::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let rhs =
BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
current_node =
Binary::new(BinaryOp::Logical(LogicalOp::And), current_node, rhs).into();
@ -310,7 +289,6 @@ where
cursor.advance(interner);
previous = PreviousExpr::Logical;
let rhs = Self::with_previous(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
@ -331,13 +309,9 @@ where
}
cursor.advance(interner);
previous = PreviousExpr::Coalesce;
let rhs = BitwiseORExpression::new(
self.name,
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let rhs =
BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
current_node =
Binary::new(BinaryOp::Logical(LogicalOp::Coalesce), current_node, rhs)
.into();
@ -359,7 +333,6 @@ where
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
#[derive(Debug, Clone, Copy)]
struct BitwiseORExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -367,15 +340,13 @@ struct BitwiseORExpression {
impl BitwiseORExpression {
/// Creates a new `BitwiseORExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -387,7 +358,7 @@ expression!(
BitwiseORExpression,
BitwiseXORExpression,
[Punctuator::Or],
[name, allow_in, allow_yield, allow_await],
[allow_in, allow_yield, allow_await],
None::<InputElement>
);
@ -401,7 +372,6 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
#[derive(Debug, Clone, Copy)]
struct BitwiseXORExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -409,15 +379,13 @@ struct BitwiseXORExpression {
impl BitwiseXORExpression {
/// Creates a new `BitwiseXORExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -429,7 +397,7 @@ expression!(
BitwiseXORExpression,
BitwiseANDExpression,
[Punctuator::Xor],
[name, allow_in, allow_yield, allow_await],
[allow_in, allow_yield, allow_await],
None::<InputElement>
);
@ -443,7 +411,6 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
#[derive(Debug, Clone, Copy)]
struct BitwiseANDExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -451,15 +418,13 @@ struct BitwiseANDExpression {
impl BitwiseANDExpression {
/// Creates a new `BitwiseANDExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -471,7 +436,7 @@ expression!(
BitwiseANDExpression,
EqualityExpression,
[Punctuator::And],
[name, allow_in, allow_yield, allow_await],
[allow_in, allow_yield, allow_await],
None::<InputElement>
);
@ -485,7 +450,6 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#sec-equality-operators
#[derive(Debug, Clone, Copy)]
struct EqualityExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -493,15 +457,13 @@ struct EqualityExpression {
impl EqualityExpression {
/// Creates a new `EqualityExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -518,7 +480,7 @@ expression!(
Punctuator::StrictEq,
Punctuator::StrictNotEq
],
[name, allow_in, allow_yield, allow_await],
[allow_in, allow_yield, allow_await],
None::<InputElement>
);
@ -532,7 +494,6 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators
#[derive(Debug, Clone, Copy)]
struct RelationalExpression {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -540,15 +501,13 @@ struct RelationalExpression {
impl RelationalExpression {
/// Creates a new `RelationalExpression` parser.
pub(super) fn new<N, I, Y, A>(name: N, allow_in: I, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -581,9 +540,8 @@ where
cursor.advance(interner);
cursor.advance(interner);
let rhs =
ShiftExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let rhs = ShiftExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(BinaryInPrivate::new(PrivateName::new(identifier), rhs).into());
}
@ -592,8 +550,8 @@ where
}
}
let mut lhs = ShiftExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let mut lhs =
ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
while let Some(tok) = cursor.peek(0, interner)? {
match *tok.kind() {
@ -607,7 +565,7 @@ where
lhs = Binary::new(
op.as_binary_op().expect("Could not get binary operation."),
lhs,
ShiftExpression::new(self.name, self.allow_yield, self.allow_await)
ShiftExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
.into();
@ -626,7 +584,7 @@ where
lhs = Binary::new(
op.as_binary_op().expect("Could not get binary operation."),
lhs,
ShiftExpression::new(self.name, self.allow_yield, self.allow_await)
ShiftExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
.into();
@ -649,21 +607,18 @@ where
/// [spec]: https://tc39.es/ecma262/#sec-bitwise-shift-operators
#[derive(Debug, Clone, Copy)]
struct ShiftExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ShiftExpression {
/// Creates a new `ShiftExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -678,7 +633,7 @@ expression!(
Punctuator::RightSh,
Punctuator::URightSh
],
[name, allow_yield, allow_await],
[allow_yield, allow_await],
None::<InputElement>
);
@ -694,21 +649,18 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#sec-additive-operators
#[derive(Debug, Clone, Copy)]
struct AdditiveExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl AdditiveExpression {
/// Creates a new `AdditiveExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -719,7 +671,7 @@ expression!(
AdditiveExpression,
MultiplicativeExpression,
[Punctuator::Add, Punctuator::Sub],
[name, allow_yield, allow_await],
[allow_yield, allow_await],
None::<InputElement>
);
@ -735,21 +687,18 @@ expression!(
/// [spec]: https://tc39.es/ecma262/#sec-multiplicative-operators
#[derive(Debug, Clone, Copy)]
struct MultiplicativeExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl MultiplicativeExpression {
/// Creates a new `MultiplicativeExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -760,7 +709,7 @@ expression!(
MultiplicativeExpression,
ExponentiationExpression,
[Punctuator::Mul, Punctuator::Div, Punctuator::Mod],
[name, allow_yield, allow_await],
[allow_yield, allow_await],
Some(InputElement::Div)
);

10
core/parser/src/parser/expression/primary/array_initializer/mod.rs

@ -99,9 +99,8 @@ where
}
TokenKind::Punctuator(Punctuator::Spread) => {
cursor.advance(interner);
let node =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
elements.push(Some(Spread::new(node).into()));
next_comma = true;
last_spread = true;
@ -114,9 +113,8 @@ where
));
}
_ => {
let expr =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expr = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
elements.push(Some(expr));
next_comma = true;
last_spread = false;

18
core/parser/src/parser/expression/primary/async_function_expression/mod.rs

@ -12,8 +12,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::Identifier,
function::AsyncFunction,
function::AsyncFunctionExpression as AsyncFunctionExpressionNode,
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol},
Keyword, Punctuator,
};
@ -29,17 +28,12 @@ use boa_profiler::Profiler;
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function
/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncFunctionExpression {
name: Option<Identifier>,
}
pub(super) struct AsyncFunctionExpression {}
impl AsyncFunctionExpression {
/// Creates a new `AsyncFunctionExpression` parser.
pub(super) fn new<N>(name: N) -> Self
where
N: Into<Option<Identifier>>,
{
Self { name: name.into() }
pub(super) fn new() -> Self {
Self {}
}
}
@ -47,7 +41,7 @@ impl<R> TokenParser<R> for AsyncFunctionExpression
where
R: ReadChar,
{
type Output = AsyncFunction;
type Output = AsyncFunctionExpressionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("AsyncFunctionExpression", "Parsing");
@ -145,7 +139,7 @@ where
interner,
)?;
let function = AsyncFunction::new(name.or(self.name), params, body, name.is_some());
let function = AsyncFunctionExpressionNode::new(name, params, body, name.is_some());
if contains(&function, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax(

8
core/parser/src/parser/expression/primary/async_function_expression/tests.rs

@ -2,7 +2,7 @@ use crate::parser::tests::check_script_parser;
use boa_ast::{
declaration::{Declaration, LexicalDeclaration, Variable},
expression::literal::Literal,
function::{AsyncFunction, FormalParameterList, FunctionBody},
function::{AsyncFunctionExpression, FormalParameterList, FunctionBody},
statement::Return,
Statement, StatementListItem,
};
@ -23,7 +23,7 @@ fn check_async_expression() {
vec![Variable::from_identifier(
add.into(),
Some(
AsyncFunction::new(
AsyncFunctionExpression::new(
Some(add.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -61,7 +61,7 @@ fn check_nested_async_expression() {
vec![Variable::from_identifier(
a.into(),
Some(
AsyncFunction::new(
AsyncFunctionExpression::new(
Some(a.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -69,7 +69,7 @@ fn check_nested_async_expression() {
vec![Variable::from_identifier(
b.into(),
Some(
AsyncFunction::new(
AsyncFunctionExpression::new(
Some(b.into()),
FormalParameterList::default(),
FunctionBody::new(

18
core/parser/src/parser/expression/primary/async_generator_expression/mod.rs

@ -21,8 +21,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::Identifier,
function::AsyncGenerator,
function::AsyncGeneratorExpression as AsyncGeneratorExpressionNode,
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol},
Keyword, Punctuator,
};
@ -36,17 +35,12 @@ use boa_profiler::Profiler;
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncGeneratorExpression {
name: Option<Identifier>,
}
pub(super) struct AsyncGeneratorExpression {}
impl AsyncGeneratorExpression {
/// Creates a new `AsyncGeneratorExpression` parser.
pub(in crate::parser) fn new<N>(name: N) -> Self
where
N: Into<Option<Identifier>>,
{
Self { name: name.into() }
pub(in crate::parser) fn new() -> Self {
Self {}
}
}
@ -55,7 +49,7 @@ where
R: ReadChar,
{
//The below needs to be implemented in ast::node
type Output = AsyncGenerator;
type Output = AsyncGeneratorExpressionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("AsyncGeneratorExpression", "Parsing");
@ -182,7 +176,7 @@ where
interner,
)?;
let function = AsyncGenerator::new(name.or(self.name), params, body, name.is_some());
let function = AsyncGeneratorExpressionNode::new(name, params, body, name.is_some());
if contains(&function, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax(

8
core/parser/src/parser/expression/primary/async_generator_expression/tests.rs

@ -2,7 +2,7 @@ use crate::parser::tests::check_script_parser;
use boa_ast::{
declaration::{LexicalDeclaration, Variable},
expression::literal::Literal,
function::{AsyncGenerator, FormalParameterList, FunctionBody},
function::{AsyncGeneratorExpression, FormalParameterList, FunctionBody},
statement::Return,
Declaration, Statement, StatementListItem,
};
@ -24,7 +24,7 @@ fn check_async_generator_expr() {
vec![Variable::from_identifier(
add.into(),
Some(
AsyncGenerator::new(
AsyncGeneratorExpression::new(
Some(add.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -62,7 +62,7 @@ fn check_nested_async_generator_expr() {
vec![Variable::from_identifier(
a.into(),
Some(
AsyncGenerator::new(
AsyncGeneratorExpression::new(
Some(a.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -70,7 +70,7 @@ fn check_nested_async_generator_expr() {
vec![Variable::from_identifier(
b.into(),
Some(
AsyncGenerator::new(
AsyncGeneratorExpression::new(
Some(b.into()),
FormalParameterList::default(),
FunctionBody::new(

28
core/parser/src/parser/expression/primary/class_expression/mod.rs

@ -6,7 +6,7 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{expression::Identifier, function::Class, Keyword};
use boa_ast::{function::ClassExpression as ClassExpressionNode, Keyword};
use boa_interner::Interner;
use boa_profiler::Profiler;
@ -18,21 +18,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-ClassExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct ClassExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ClassExpression {
/// Creates a new `ClassExpression` parser.
pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -43,33 +40,34 @@ impl<R> TokenParser<R> for ClassExpression
where
R: ReadChar,
{
type Output = Class;
type Output = ClassExpressionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("ClassExpression", "Parsing");
let strict = cursor.strict();
cursor.set_strict(true);
let mut has_binding_identifier = false;
let token = cursor.peek(0, interner).or_abrupt()?;
let name = match token.kind() {
TokenKind::IdentifierName(_)
| TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {
has_binding_identifier = true;
BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?
.into()
}
_ => self.name,
_ => None,
};
cursor.set_strict(strict);
ClassTail::new(
let (super_ref, constructor, elements) =
ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?;
Ok(ClassExpressionNode::new(
name,
has_binding_identifier,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)
super_ref,
constructor,
elements.into_boxed_slice(),
name.is_some(),
))
}
}

19
core/parser/src/parser/expression/primary/function_expression/mod.rs

@ -21,8 +21,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::Identifier,
function::Function,
function::FunctionExpression as FunctionExpressionNode,
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol},
Keyword, Punctuator,
};
@ -38,17 +37,12 @@ use boa_profiler::Profiler;
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct FunctionExpression {
name: Option<Identifier>,
}
pub(super) struct FunctionExpression {}
impl FunctionExpression {
/// Creates a new `FunctionExpression` parser.
pub(in crate::parser) fn new<N>(name: N) -> Self
where
N: Into<Option<Identifier>>,
{
Self { name: name.into() }
pub(in crate::parser) fn new() -> Self {
Self {}
}
}
@ -56,7 +50,7 @@ impl<R> TokenParser<R> for FunctionExpression
where
R: ReadChar,
{
type Output = Function;
type Output = FunctionExpressionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("FunctionExpression", "Parsing");
@ -140,8 +134,7 @@ where
interner,
)?;
let function =
Function::new_with_binding_identifier(name.or(self.name), params, body, name.is_some());
let function = FunctionExpressionNode::new(name, params, body, name.is_some());
if contains(&function, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax(

13
core/parser/src/parser/expression/primary/function_expression/tests.rs

@ -2,7 +2,7 @@ use crate::parser::tests::check_script_parser;
use boa_ast::{
declaration::{LexicalDeclaration, Variable},
expression::literal::Literal,
function::{FormalParameterList, Function, FunctionBody},
function::{FormalParameterList, FunctionBody, FunctionExpression},
statement::Return,
Declaration, Statement, StatementListItem,
};
@ -23,7 +23,7 @@ fn check_function_expression() {
vec![Variable::from_identifier(
add.into(),
Some(
Function::new(
FunctionExpression::new(
Some(add.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -32,6 +32,7 @@ fn check_function_expression() {
))]
.into(),
),
false,
)
.into(),
),
@ -60,7 +61,7 @@ fn check_nested_function_expression() {
vec![Variable::from_identifier(
a.into(),
Some(
Function::new(
FunctionExpression::new(
Some(a.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -68,7 +69,7 @@ fn check_nested_function_expression() {
vec![Variable::from_identifier(
b.into(),
Some(
Function::new(
FunctionExpression::new(
Some(b.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -79,6 +80,7 @@ fn check_nested_function_expression() {
)]
.into(),
),
false,
)
.into(),
),
@ -89,6 +91,7 @@ fn check_nested_function_expression() {
.into()]
.into(),
),
false,
)
.into(),
),
@ -109,7 +112,7 @@ fn check_function_non_reserved_keyword() {
vec![Variable::from_identifier(
$interner.get_or_intern_static("add", utf16!("add")).into(),
Some(
Function::new_with_binding_identifier(
FunctionExpression::new(
Some($interner.get_or_intern_static($keyword, utf16!($keyword)).into()),
FormalParameterList::default(),
FunctionBody::new(

18
core/parser/src/parser/expression/primary/generator_expression/mod.rs

@ -21,8 +21,7 @@ use crate::{
Error,
};
use boa_ast::{
expression::Identifier,
function::Generator,
function::GeneratorExpression as GeneratorExpressionNode,
operations::{bound_names, contains, lexically_declared_names, ContainsSymbol},
Keyword, Punctuator,
};
@ -38,17 +37,12 @@ use boa_profiler::Profiler;
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function*
/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct GeneratorExpression {
name: Option<Identifier>,
}
pub(super) struct GeneratorExpression {}
impl GeneratorExpression {
/// Creates a new `GeneratorExpression` parser.
pub(in crate::parser) fn new<N>(name: N) -> Self
where
N: Into<Option<Identifier>>,
{
Self { name: name.into() }
pub(in crate::parser) fn new() -> Self {
Self {}
}
}
@ -56,7 +50,7 @@ impl<R> TokenParser<R> for GeneratorExpression
where
R: ReadChar,
{
type Output = Generator;
type Output = GeneratorExpressionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("GeneratorExpression", "Parsing");
@ -157,7 +151,7 @@ where
)));
}
let function = Generator::new(name.or(self.name), params, body, name.is_some());
let function = GeneratorExpressionNode::new(name, params, body, name.is_some());
if contains(&function, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax(

6
core/parser/src/parser/expression/primary/generator_expression/tests.rs

@ -2,7 +2,7 @@ use crate::parser::tests::check_script_parser;
use boa_ast::{
declaration::{LexicalDeclaration, Variable},
expression::{literal::Literal, Yield},
function::{FormalParameterList, FunctionBody, Generator},
function::{FormalParameterList, FunctionBody, GeneratorExpression},
Declaration, Expression, Statement, StatementListItem,
};
use boa_interner::Interner;
@ -21,7 +21,7 @@ fn check_generator_function_expression() {
vec![Variable::from_identifier(
gen.into(),
Some(
Generator::new(
GeneratorExpression::new(
Some(gen.into()),
FormalParameterList::default(),
FunctionBody::new(
@ -56,7 +56,7 @@ fn check_generator_function_delegate_yield_expression() {
vec![Variable::from_identifier(
gen.into(),
Some(
Generator::new(
GeneratorExpression::new(
Some(gen.into()),
FormalParameterList::default(),
FunctionBody::new(

26
core/parser/src/parser/expression/primary/mod.rs

@ -71,21 +71,18 @@ pub(in crate::parser) use object_initializer::Initializer;
/// [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct PrimaryExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl PrimaryExpression {
/// Creates a new `PrimaryExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -121,18 +118,18 @@ where
cursor.advance(interner);
let next_token = cursor.peek(0, interner).or_abrupt()?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
GeneratorExpression::new(self.name)
GeneratorExpression::new()
.parse(cursor, interner)
.map(Into::into)
} else {
FunctionExpression::new(self.name)
FunctionExpression::new()
.parse(cursor, interner)
.map(Into::into)
}
}
TokenKind::Keyword((Keyword::Class, _)) => {
cursor.advance(interner);
ClassExpression::new(self.name, self.allow_yield, self.allow_await)
ClassExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)
.map(Into::into)
}
@ -154,11 +151,11 @@ where
cursor.advance(interner);
match cursor.peek(1, interner)?.map(Token::kind) {
Some(TokenKind::Punctuator(Punctuator::Mul)) => {
AsyncGeneratorExpression::new(self.name)
AsyncGeneratorExpression::new()
.parse(cursor, interner)
.map(Into::into)
}
_ => AsyncFunctionExpression::new(self.name)
_ => AsyncFunctionExpression::new()
.parse(cursor, interner)
.map(Into::into),
}
@ -172,7 +169,6 @@ where
cursor.advance(interner);
cursor.set_goal(InputElement::RegExp);
let expr = CoverParenthesizedExpressionAndArrowParameterList::new(
self.name,
self.allow_yield,
self.allow_await,
)
@ -295,21 +291,18 @@ where
/// [spec]: https://tc39.es/ecma262/#prod-CoverParenthesizedExpressionAndArrowParameterList
#[derive(Debug, Clone, Copy)]
pub(super) struct CoverParenthesizedExpressionAndArrowParameterList {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl CoverParenthesizedExpressionAndArrowParameterList {
/// Creates a new `CoverParenthesizedExpressionAndArrowParameterList` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -379,9 +372,8 @@ where
.span()
}
_ => {
let expression =
Expression::new(self.name, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expression = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
expressions.push(InnerExpression::Expression(expression));
let next = cursor.peek(0, interner).or_abrupt()?;

299
core/parser/src/parser/expression/primary/object_initializer/mod.rs

@ -26,17 +26,17 @@ use crate::{
};
use boa_ast::{
expression::{
literal::{self, Literal},
literal::{
self, Literal, ObjectMethodDefinition, PropertyDefinition as PropertyDefinitionNode,
},
Identifier,
},
function::{
AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator, PrivateName,
},
function::{ClassElementName as ClassElementNameNode, FormalParameterList, PrivateName},
operations::{
bound_names, contains, has_direct_super, lexically_declared_names, ContainsSymbol,
bound_names, contains, has_direct_super_new, lexically_declared_names, ContainsSymbol,
},
property::{self, MethodDefinition},
Expression, Keyword, Punctuator,
property::{MethodDefinitionKind, PropertyName as PropertyNameNode},
Expression, Keyword, Punctuator, Script,
};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
@ -94,10 +94,7 @@ where
if matches!(
property,
property::PropertyDefinition::Property(
property::PropertyName::Literal(Sym::__PROTO__),
_
)
PropertyDefinitionNode::Property(PropertyNameNode::Literal(Sym::__PROTO__), _)
) {
if has_proto && duplicate_proto_position.is_none() {
duplicate_proto_position = Some(position);
@ -170,7 +167,7 @@ impl<R> TokenParser<R> for PropertyDefinition
where
R: ReadChar,
{
type Output = property::PropertyDefinition;
type Output = PropertyDefinitionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("PropertyDefinition", "Parsing");
@ -179,7 +176,7 @@ where
TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma) => {
let ident = IdentifierReference::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(property::PropertyDefinition::IdentifierReference(ident));
return Ok(PropertyDefinitionNode::IdentifierReference(ident));
}
TokenKind::Punctuator(Punctuator::Assign) => {
return CoverInitializedName::new(self.allow_yield, self.allow_await)
@ -190,9 +187,9 @@ where
// ... AssignmentExpression[+In, ?Yield, ?Await]
if cursor.next_if(Punctuator::Spread, interner)?.is_some() {
let node = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(property::PropertyDefinition::SpreadObject(node));
return Ok(PropertyDefinitionNode::SpreadObject(node));
}
//Async [AsyncMethod, AsyncGeneratorMethod] object methods
@ -217,17 +214,11 @@ where
let position = token.span().start();
if token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
let (class_element_name, method) =
let (class_element_name, params, body) =
AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", position));
}
let property::ClassElementName::PropertyName(property_name) =
class_element_name
let ClassElementNameNode::PropertyName(property_name) = class_element_name
else {
return Err(Error::general(
"private identifiers not allowed in object literal",
@ -235,30 +226,48 @@ where
));
};
return Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
return Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
params,
body,
MethodDefinitionKind::AsyncGenerator,
),
));
}
let (class_element_name, method) =
let (class_element_name, params, body) =
AsyncMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
let property::ClassElementName::PropertyName(property_name) = class_element_name
else {
let ClassElementNameNode::PropertyName(property_name) = class_element_name else {
return Err(Error::general(
"private identifiers not allowed in object literal",
position,
));
};
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", position));
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
return Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
return Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
params,
body,
MethodDefinitionKind::Async,
),
));
}
_ => {}
@ -268,28 +277,32 @@ where
if token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
let position = cursor.peek(0, interner).or_abrupt()?.span().start();
let (class_element_name, method) =
let (class_element_name, params, body) =
GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", position));
}
let ClassElementNameNode::PropertyName(property_name) = class_element_name else {
return Err(Error::general(
"private identifier not allowed in object literal",
position,
));
};
match class_element_name {
property::ClassElementName::PropertyName(property_name) => {
return Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
))
}
property::ClassElementName::PrivateIdentifier(_) => {
return Err(Error::general(
"private identifier not allowed in object literal",
position,
))
}
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
return Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
params,
body,
MethodDefinitionKind::Generator,
),
));
}
let set_or_get_escaped_position = match token.kind() {
@ -304,15 +317,16 @@ where
// PropertyName[?Yield, ?Await] : AssignmentExpression[+In, ?Yield, ?Await]
if cursor.next_if(Punctuator::Colon, interner)?.is_some() {
let name = property_name
.literal()
.filter(|name| *name != Sym::__PROTO__)
.map(Into::into);
let value = AssignmentExpression::new(name, true, self.allow_yield, self.allow_await)
let mut value = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(property::PropertyDefinition::Property(property_name, value));
if let Some(name) = property_name.literal() {
if name != Sym::__PROTO__ {
value.set_anonymous_function_definition_name(&Identifier::new(name));
}
}
return Ok(PropertyDefinitionNode::Property(property_name, value));
}
let ordinary_method = cursor.peek(0, interner).or_abrupt()?.kind()
@ -320,7 +334,7 @@ where
match property_name {
// MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] }
property::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => {
PropertyNameNode::Literal(str) if str == Sym::GET && !ordinary_method => {
if let Some(position) = set_or_get_escaped_position {
return Err(Error::general(
"Keyword must not contain escaped characters",
@ -356,24 +370,25 @@ where
interner,
)?;
let method = MethodDefinition::Get(Function::new(
None,
FormalParameterList::default(),
body,
));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", position));
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&FormalParameterList::default(), &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
FormalParameterList::default(),
body,
MethodDefinitionKind::Get,
),
))
}
// MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
property::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => {
PropertyNameNode::Literal(str) if str == Sym::SET && !ordinary_method => {
if let Some(position) = set_or_get_escaped_position {
return Err(Error::general(
"Keyword must not contain escaped characters",
@ -392,7 +407,7 @@ where
)?
.span()
.end();
let parameters: FormalParameterList = FormalParameter::new(false, false)
let params: FormalParameterList = FormalParameter::new(false, false)
.parse(cursor, interner)?
.into();
cursor.expect(
@ -414,7 +429,7 @@ where
)?;
// Catch early error for BindingIdentifier.
if body.strict() && contains(&parameters, ContainsSymbol::EvalOrArguments) {
if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {
return Err(Error::lex(LexError::Syntax(
"unexpected identifier 'eval' or 'arguments' in strict mode".into(),
params_start_position,
@ -424,7 +439,7 @@ where
// It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
// and IsSimpleParameterList of PropertySetParameterList is false.
// https://tc39.es/ecma262/#sec-method-definitions-static-semantics-early-errors
if body.strict() && !parameters.is_simple() {
if body.strict() && !params.is_simple() {
return Err(Error::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
@ -436,23 +451,27 @@ where
// occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-method-definitions-static-semantics-early-errors
name_in_lexically_declared_names(
&bound_names(&parameters),
&bound_names(&params),
&lexically_declared_names(&body),
params_start_position,
interner,
)?;
let method = MethodDefinition::Set(Function::new(None, parameters, body));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
// https://tc39.es/ecma262/#sec-object-initializer-static-semantics-early-errors
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", params_start_position));
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
params_start_position,
)));
}
Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
params,
body,
MethodDefinitionKind::Set,
),
))
}
// MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
@ -511,16 +530,21 @@ where
interner,
)?;
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) {
return Err(Error::general("invalid super usage", params_start_position));
// Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
params_start_position,
)));
}
Ok(property::PropertyDefinition::MethodDefinition(
property_name,
method,
Ok(PropertyDefinitionNode::MethodDefinition(
ObjectMethodDefinition::new(
property_name,
params,
body,
MethodDefinitionKind::Ordinary,
),
))
}
}
@ -557,7 +581,7 @@ impl<R> TokenParser<R> for PropertyName
where
R: ReadChar,
{
type Output = property::PropertyName;
type Output = PropertyNameNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("PropertyName", "Parsing");
@ -566,9 +590,8 @@ where
let name = match token.kind() {
TokenKind::Punctuator(Punctuator::OpenBracket) => {
cursor.advance(interner);
let node =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "expected token ']'", interner)?;
return Ok(node.into());
}
@ -633,7 +656,7 @@ impl<R> TokenParser<R> for ClassElementName
where
R: ReadChar,
{
type Output = property::ClassElementName;
type Output = ClassElementNameNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("ClassElementName", "Parsing");
@ -643,11 +666,9 @@ where
TokenKind::PrivateIdentifier(ident) => {
let ident = *ident;
cursor.advance(interner);
Ok(property::ClassElementName::PrivateIdentifier(
PrivateName::new(ident),
))
Ok(ClassElementNameNode::PrivateName(PrivateName::new(ident)))
}
_ => Ok(property::ClassElementName::PropertyName(
_ => Ok(ClassElementNameNode::PropertyName(
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?,
)),
}
@ -662,7 +683,6 @@ where
/// [spec]: https://tc39.es/ecma262/#prod-Initializer
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct Initializer {
name: Option<Identifier>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
@ -670,20 +690,13 @@ pub(in crate::parser) struct Initializer {
impl Initializer {
/// Creates a new `Initializer` parser.
pub(in crate::parser) fn new<N, I, Y, A>(
name: N,
allow_in: I,
allow_yield: Y,
allow_await: A,
) -> Self
pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
@ -701,7 +714,7 @@ where
let _timer = Profiler::global().start_event("Initializer", "Parsing");
cursor.expect(Punctuator::Assign, "initializer", interner)?;
AssignmentExpression::new(self.name, self.allow_in, self.allow_yield, self.allow_await)
AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)
}
}
@ -736,7 +749,7 @@ impl<R> TokenParser<R> for GeneratorMethod
where
R: ReadChar,
{
type Output = (property::ClassElementName, MethodDefinition);
type Output = (ClassElementNameNode, FormalParameterList, Script);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("GeneratorMethod", "Parsing");
@ -790,16 +803,15 @@ where
interner,
)?;
let method = MethodDefinition::Generator(Generator::new(None, params, body, false));
if contains(&method, ContainsSymbol::Super) {
// Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
"invalid super call usage".into(),
body_start,
)));
}
Ok((class_element_name, method))
Ok((class_element_name, params, body))
}
}
@ -833,7 +845,7 @@ impl<R> TokenParser<R> for AsyncGeneratorMethod
where
R: ReadChar,
{
type Output = (property::ClassElementName, MethodDefinition);
type Output = (ClassElementNameNode, FormalParameterList, Script);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("AsyncGeneratorMethod", "Parsing");
@ -850,7 +862,7 @@ where
let params = UniqueFormalParameters::new(true, true).parse(cursor, interner)?;
// It is a Syntax Error if FormalParameters Contains YieldExpression is true.
// Early Error: It is a Syntax Error if UniqueFormalParameters Contains YieldExpression is true.
if contains(&params, ContainsSymbol::YieldExpression) {
return Err(Error::lex(LexError::Syntax(
"yield expression not allowed in async generator method definition parameters"
@ -859,7 +871,7 @@ where
)));
}
// It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
// Early Error: It is a Syntax Error if UniqueFormalParameters Contains AwaitExpression is true.
if contains(&params, ContainsSymbol::AwaitExpression) {
return Err(Error::lex(LexError::Syntax(
"await expression not allowed in async generator method definition parameters"
@ -892,8 +904,8 @@ where
)));
}
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters
// also occurs in the LexicallyDeclaredNames of AsyncGeneratorBody.
name_in_lexically_declared_names(
&bound_names(&params),
&lexically_declared_names(&body),
@ -901,17 +913,15 @@ where
interner,
)?;
let method =
MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body, false));
if contains(&method, ContainsSymbol::Super) {
// Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
"invalid super call usage".into(),
body_start,
)));
}
Ok((name, method))
Ok((name, params, body))
}
}
@ -945,7 +955,7 @@ impl<R> TokenParser<R> for AsyncMethod
where
R: ReadChar,
{
type Output = (property::ClassElementName, MethodDefinition);
type Output = (ClassElementNameNode, FormalParameterList, Script);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("AsyncMethod", "Parsing");
@ -972,8 +982,8 @@ where
interner,
)?;
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
// and IsSimpleParameterList of UniqueFormalParameters is false.
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of AsyncFunctionBody
// is true and IsSimpleParameterList of UniqueFormalParameters is false.
if body.strict() && !params.is_simple() {
return Err(Error::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
@ -981,8 +991,8 @@ where
)));
}
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters
// also occurs in the LexicallyDeclaredNames of AsyncFunctionBody.
name_in_lexically_declared_names(
&bound_names(&params),
&lexically_declared_names(&body),
@ -990,16 +1000,15 @@ where
interner,
)?;
let method = MethodDefinition::Async(AsyncFunction::new(None, params, body, false));
if contains(&method, ContainsSymbol::Super) {
// Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.
if has_direct_super_new(&params, &body) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
"invalid super call usage".into(),
body_start,
)));
}
Ok((class_element_name, method))
Ok((class_element_name, params, body))
}
}
@ -1033,7 +1042,7 @@ impl<R> TokenParser<R> for CoverInitializedName
where
R: ReadChar,
{
type Output = property::PropertyDefinition;
type Output = PropertyDefinitionNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("CoverInitializedName", "Parsing");
@ -1043,11 +1052,9 @@ where
cursor.expect(Punctuator::Assign, "CoverInitializedName", interner)?;
let expr = AssignmentExpression::new(ident, true, self.allow_yield, self.allow_await)
let expr = AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
Ok(property::PropertyDefinition::CoverInitializedName(
ident, expr,
))
Ok(PropertyDefinitionNode::CoverInitializedName(ident, expr))
}
}

93
core/parser/src/parser/expression/primary/object_initializer/tests.rs

@ -2,14 +2,11 @@ use crate::parser::tests::{check_invalid_script, check_script_parser};
use boa_ast::{
declaration::{LexicalDeclaration, Variable},
expression::{
literal::{Literal, ObjectLiteral},
literal::{Literal, ObjectLiteral, ObjectMethodDefinition, PropertyDefinition},
Identifier,
},
function::{
AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList,
FormalParameterListFlags, Function, FunctionBody,
},
property::{MethodDefinition, PropertyDefinition, PropertyName},
function::{FormalParameter, FormalParameterList, FormalParameterListFlags, FunctionBody},
property::{MethodDefinitionKind, PropertyName},
Declaration,
};
use boa_interner::{Interner, Sym};
@ -60,14 +57,12 @@ fn check_object_short_function() {
interner.get_or_intern_static("a", utf16!("a")).into(),
Literal::from(true).into(),
),
PropertyDefinition::MethodDefinition(
PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(
interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Ordinary(Function::new(
None,
FormalParameterList::default(),
FunctionBody::default(),
)),
),
FormalParameterList::default(),
FunctionBody::default(),
MethodDefinitionKind::Ordinary,
)),
];
check_script_parser(
@ -110,10 +105,12 @@ fn check_object_short_function_arguments() {
interner.get_or_intern_static("a", utf16!("a")).into(),
Literal::from(true).into(),
),
PropertyDefinition::MethodDefinition(
PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(
interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Ordinary(Function::new(None, parameters, FunctionBody::default())),
),
parameters,
FunctionBody::default(),
MethodDefinitionKind::Ordinary,
)),
];
check_script_parser(
@ -144,14 +141,12 @@ fn check_object_getter() {
interner.get_or_intern_static("a", utf16!("a")).into(),
Literal::from(true).into(),
),
PropertyDefinition::MethodDefinition(
PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(
interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Get(Function::new(
None,
FormalParameterList::default(),
FunctionBody::default(),
)),
),
FormalParameterList::default(),
FunctionBody::default(),
MethodDefinitionKind::Get,
)),
];
check_script_parser(
@ -193,10 +188,12 @@ fn check_object_setter() {
interner.get_or_intern_static("a", utf16!("a")).into(),
Literal::from(true).into(),
),
PropertyDefinition::MethodDefinition(
PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(
interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Set(Function::new(None, params, FunctionBody::default())),
),
params,
FunctionBody::default(),
MethodDefinitionKind::Set,
)),
];
check_script_parser(
@ -223,12 +220,12 @@ fn check_object_short_function_get() {
let interner = &mut Interner::default();
let object_properties = vec![PropertyDefinition::MethodDefinition(
Sym::GET.into(),
MethodDefinition::Ordinary(Function::new(
None,
ObjectMethodDefinition::new(
Sym::GET.into(),
FormalParameterList::default(),
FunctionBody::default(),
)),
MethodDefinitionKind::Ordinary,
),
)];
check_script_parser(
@ -254,12 +251,12 @@ fn check_object_short_function_set() {
let interner = &mut Interner::default();
let object_properties = vec![PropertyDefinition::MethodDefinition(
Sym::SET.into(),
MethodDefinition::Ordinary(Function::new(
None,
ObjectMethodDefinition::new(
Sym::SET.into(),
FormalParameterList::default(),
FunctionBody::default(),
)),
MethodDefinitionKind::Ordinary,
),
)];
check_script_parser(
@ -402,13 +399,12 @@ fn check_async_method() {
let interner = &mut Interner::default();
let object_properties = vec![PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("dive", utf16!("dive")).into(),
MethodDefinition::Async(AsyncFunction::new(
None,
ObjectMethodDefinition::new(
PropertyName::Literal(interner.get_or_intern_static("dive", utf16!("dive"))),
FormalParameterList::default(),
FunctionBody::default(),
false,
)),
MethodDefinitionKind::Async,
),
)];
check_script_parser(
@ -434,15 +430,12 @@ fn check_async_generator_method() {
let interner = &mut Interner::default();
let object_properties = vec![PropertyDefinition::MethodDefinition(
interner
.get_or_intern_static("vroom", utf16!("vroom"))
.into(),
MethodDefinition::AsyncGenerator(AsyncGenerator::new(
None,
ObjectMethodDefinition::new(
PropertyName::Literal(interner.get_or_intern_static("vroom", utf16!("vroom"))),
FormalParameterList::default(),
FunctionBody::default(),
false,
)),
MethodDefinitionKind::AsyncGenerator,
),
)];
check_script_parser(
@ -490,12 +483,12 @@ fn check_async_ordinary_method() {
let interner = &mut Interner::default();
let object_properties = vec![PropertyDefinition::MethodDefinition(
PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))),
MethodDefinition::Ordinary(Function::new(
None,
ObjectMethodDefinition::new(
PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))),
FormalParameterList::default(),
FunctionBody::default(),
)),
MethodDefinitionKind::Ordinary,
),
)];
check_script_parser(

4
core/parser/src/parser/expression/primary/template/mod.rs

@ -64,7 +64,7 @@ where
let mut elements = vec![
TemplateElement::String(self.first),
TemplateElement::Expr(
Expression::new(None, true, self.allow_yield, self.allow_await)
Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
),
];
@ -85,7 +85,7 @@ where
};
elements.push(TemplateElement::String(cooked));
elements.push(TemplateElement::Expr(
Expression::new(None, true, self.allow_yield, self.allow_await)
Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
));
cursor.expect(

9
core/parser/src/parser/expression/unary.rs

@ -20,7 +20,6 @@ use boa_ast::{
expression::{
access::PropertyAccess,
operator::{unary::UnaryOp, Unary},
Identifier,
},
Expression, Keyword, Punctuator,
};
@ -37,21 +36,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct UnaryExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl UnaryExpression {
/// Creates a new `UnaryExpression` parser.
pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -129,8 +125,7 @@ where
TokenKind::Keyword((Keyword::Await, false)) if self.allow_await.0 => {
Ok((AwaitExpression::new(self.allow_yield).parse(cursor, interner)?).into())
}
_ => UpdateExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner),
_ => UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner),
}
}
}

20
core/parser/src/parser/expression/update.rs

@ -18,12 +18,9 @@ use crate::{
Error,
};
use boa_ast::{
expression::{
operator::{
update::{UpdateOp, UpdateTarget},
Update,
},
Identifier,
expression::operator::{
update::{UpdateOp, UpdateTarget},
Update,
},
Expression, Position, Punctuator,
};
@ -38,21 +35,18 @@ use boa_profiler::Profiler;
/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct UpdateExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl UpdateExpression {
/// Creates a new `UpdateExpression` parser.
pub(super) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -102,7 +96,7 @@ where
.next(interner)?
.expect("Punctuator::Inc token disappeared");
let target = UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
let target = UnaryExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
@ -121,7 +115,7 @@ where
.next(interner)?
.expect("Punctuator::Dec token disappeared");
let target = UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
let target = UnaryExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
@ -138,7 +132,7 @@ where
_ => {}
}
let lhs = LeftHandSideExpression::new(self.name, self.allow_yield, self.allow_await)
let lhs = LeftHandSideExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if cursor.peek_is_line_terminator(0, interner)?.unwrap_or(true) {

10
core/parser/src/parser/function/mod.rs

@ -267,7 +267,7 @@ where
*t.kind() == TokenKind::Punctuator(Punctuator::Assign)
})
.map(|_| {
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)
})
.transpose()?;
@ -292,7 +292,7 @@ where
*t.kind() == TokenKind::Punctuator(Punctuator::Assign)
})
.map(|_| {
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)
})
.transpose()?;
@ -356,7 +356,7 @@ where
== TokenKind::Punctuator(Punctuator::Assign)
{
Some(
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -372,7 +372,7 @@ where
== TokenKind::Punctuator(Punctuator::Assign)
{
Some(
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -388,7 +388,7 @@ where
tok.kind() == &TokenKind::Punctuator(Punctuator::Assign)
}) {
Some(
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {

34
core/parser/src/parser/function/tests.rs

@ -6,8 +6,8 @@ use boa_ast::{
Identifier,
},
function::{
ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function,
FunctionBody,
ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags,
FunctionBody, FunctionDeclaration,
},
statement::Return,
Declaration, Expression, Statement, StatementListItem,
@ -28,8 +28,8 @@ fn check_basic() {
check_script_parser(
"function foo(a) { return a; }",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -39,7 +39,6 @@ fn check_basic() {
))]
.into(),
),
true,
))
.into()],
interner,
@ -67,8 +66,8 @@ fn check_duplicates_strict_off() {
assert_eq!(params.length(), 2);
check_script_parser(
"function foo(a, a) { return a; }",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -78,7 +77,6 @@ fn check_duplicates_strict_off() {
))]
.into(),
),
true,
))
.into()],
interner,
@ -104,8 +102,8 @@ fn check_basic_semicolon_insertion() {
check_script_parser(
"function foo(a) { return a }",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -115,7 +113,6 @@ fn check_basic_semicolon_insertion() {
))]
.into(),
),
true,
))
.into()],
interner,
@ -134,8 +131,8 @@ fn check_empty_return() {
assert_eq!(params.length(), 1);
check_script_parser(
"function foo(a) { return; }",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -143,7 +140,6 @@ fn check_empty_return() {
))]
.into(),
),
true,
))
.into()],
interner,
@ -162,8 +158,8 @@ fn check_empty_return_semicolon_insertion() {
assert_eq!(params.length(), 1);
check_script_parser(
"function foo(a) { return }",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -171,7 +167,6 @@ fn check_empty_return_semicolon_insertion() {
))]
.into(),
),
true,
))
.into()],
interner,
@ -199,11 +194,10 @@ fn check_rest_operator() {
assert_eq!(params.length(), 1);
check_script_parser(
"function foo(a, ...b) {}",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner.get_or_intern_static("foo", utf16!("foo")).into(),
params,
FunctionBody::default(),
true,
))
.into()],
interner,

12
core/parser/src/parser/statement/block/tests.rs

@ -12,7 +12,7 @@ use boa_ast::{
},
Call, Identifier,
},
function::{FormalParameterList, Function, FunctionBody},
function::{FormalParameterList, FunctionBody, FunctionDeclaration},
statement::{Block, Return},
Declaration, Expression, Statement, StatementListItem,
};
@ -78,8 +78,8 @@ fn non_empty() {
a++;
}",
vec![
Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()),
Declaration::FunctionDeclaration(FunctionDeclaration::new(
hello.into(),
FormalParameterList::default(),
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -87,7 +87,6 @@ fn non_empty() {
))]
.into(),
),
true,
))
.into(),
Statement::Var(VarDeclaration(
@ -136,8 +135,8 @@ fn hoisting() {
UpdateTarget::Identifier(Identifier::new(a)),
)))
.into(),
Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()),
Declaration::FunctionDeclaration(FunctionDeclaration::new(
hello.into(),
FormalParameterList::default(),
FunctionBody::new(
vec![StatementListItem::Statement(Statement::Return(
@ -145,7 +144,6 @@ fn hoisting() {
))]
.into(),
),
true,
))
.into(),
],

12
core/parser/src/parser/statement/declaration/export.rs

@ -173,12 +173,12 @@ where
TokenKind::Keyword((Keyword::Function, false)) => {
let next_token = cursor.peek(1, interner).or_abrupt()?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
AstExportDeclaration::DefaultGenerator(
AstExportDeclaration::DefaultGeneratorDeclaration(
GeneratorDeclaration::new(false, true, true)
.parse(cursor, interner)?,
)
} else {
AstExportDeclaration::DefaultFunction(
AstExportDeclaration::DefaultFunctionDeclaration(
FunctionDeclaration::new(false, true, true)
.parse(cursor, interner)?,
)
@ -187,12 +187,12 @@ where
TokenKind::Keyword((Keyword::Async, false)) => {
let next_token = cursor.peek(2, interner).or_abrupt()?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
AstExportDeclaration::DefaultAsyncGenerator(
AstExportDeclaration::DefaultAsyncGeneratorDeclaration(
AsyncGeneratorDeclaration::new(false, true, true)
.parse(cursor, interner)?,
)
} else {
AstExportDeclaration::DefaultAsyncFunction(
AstExportDeclaration::DefaultAsyncFunctionDeclaration(
AsyncFunctionDeclaration::new(false, true, true)
.parse(cursor, interner)?,
)
@ -204,8 +204,8 @@ where
)
}
_ => {
let expr = AssignmentExpression::new(None, true, false, true)
.parse(cursor, interner)?;
let expr =
AssignmentExpression::new(true, false, true).parse(cursor, interner)?;
cursor.expect_semicolon("default expression export", interner)?;

10
core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs

@ -8,7 +8,7 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{function::AsyncFunction, Keyword};
use boa_ast::{function::AsyncFunctionDeclaration as AsyncFunctionDeclarationNode, Keyword};
use boa_interner::Interner;
/// Async Function declaration parsing.
@ -27,7 +27,7 @@ pub(in crate::parser) struct AsyncFunctionDeclaration {
}
impl AsyncFunctionDeclaration {
/// Creates a new `FunctionDeclaration` parser.
/// Creates a new `AsyncFunctionDeclaration` parser.
pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
@ -76,7 +76,7 @@ impl<R> TokenParser<R> for AsyncFunctionDeclaration
where
R: ReadChar,
{
type Output = AsyncFunction;
type Output = AsyncFunctionDeclarationNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect(
@ -93,6 +93,8 @@ where
let result = parse_callable_declaration(&self, cursor, interner)?;
Ok(AsyncFunction::new(Some(result.0), result.1, result.2, true))
Ok(AsyncFunctionDeclarationNode::new(
result.0, result.1, result.2,
))
}
}

47
core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs

@ -1,6 +1,6 @@
use crate::parser::tests::check_script_parser;
use boa_ast::{
function::{AsyncFunction, FormalParameterList, FunctionBody},
function::{AsyncFunctionDeclaration, FormalParameterList, FunctionBody},
Declaration,
};
use boa_interner::{Interner, Sym};
@ -12,17 +12,16 @@ fn async_function_declaration() {
let interner = &mut Interner::default();
check_script_parser(
"async function hello() {}",
vec![Declaration::AsyncFunction(AsyncFunction::new(
Some(
vec![
Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(
interner
.get_or_intern_static("hello", utf16!("hello"))
.into(),
),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
FormalParameterList::default(),
FunctionBody::default(),
))
.into(),
],
interner,
);
}
@ -33,26 +32,28 @@ fn async_function_declaration_keywords() {
let interner = &mut Interner::default();
check_script_parser(
"async function yield() {}",
vec![Declaration::AsyncFunction(AsyncFunction::new(
Some(Sym::YIELD.into()),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
vec![
Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(
Sym::YIELD.into(),
FormalParameterList::default(),
FunctionBody::default(),
))
.into(),
],
interner,
);
let interner = &mut Interner::default();
check_script_parser(
"async function await() {}",
vec![Declaration::AsyncFunction(AsyncFunction::new(
Some(Sym::AWAIT.into()),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
vec![
Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(
Sym::AWAIT.into(),
FormalParameterList::default(),
FunctionBody::default(),
))
.into(),
],
interner,
);
}

13
core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs

@ -13,7 +13,9 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{function::AsyncGenerator, Keyword, Punctuator};
use boa_ast::{
function::AsyncGeneratorDeclaration as AsyncGeneratorDeclarationNode, Keyword, Punctuator,
};
use boa_interner::Interner;
/// Async Generator Declaration Parser
@ -89,7 +91,7 @@ impl<R> TokenParser<R> for AsyncGeneratorDeclaration
where
R: ReadChar,
{
type Output = AsyncGenerator;
type Output = AsyncGeneratorDeclarationNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect(
@ -107,11 +109,8 @@ where
let result = parse_callable_declaration(&self, cursor, interner)?;
Ok(AsyncGenerator::new(
Some(result.0),
result.1,
result.2,
true,
Ok(AsyncGeneratorDeclarationNode::new(
result.0, result.1, result.2,
))
}
}

17
core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs

@ -1,6 +1,6 @@
use crate::parser::tests::check_script_parser;
use boa_ast::{
function::{AsyncGenerator, FormalParameterList, FunctionBody},
function::{AsyncGeneratorDeclaration, FormalParameterList, FunctionBody},
Declaration,
};
use boa_interner::Interner;
@ -11,13 +11,14 @@ fn async_generator_function_declaration() {
let interner = &mut Interner::default();
check_script_parser(
"async function* gen() {}",
vec![Declaration::AsyncGenerator(AsyncGenerator::new(
Some(interner.get_or_intern_static("gen", utf16!("gen")).into()),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
vec![
Declaration::AsyncGeneratorDeclaration(AsyncGeneratorDeclaration::new(
interner.get_or_intern_static("gen", utf16!("gen")).into(),
FormalParameterList::default(),
FunctionBody::default(),
))
.into(),
],
interner,
);
}

692
core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -20,13 +20,16 @@ use ast::{
operations::{
check_labels, contains_invalid_object_literal, lexically_declared_names, var_declared_names,
},
property::MethodDefinitionKind,
};
use boa_ast::{
self as ast,
expression::Identifier,
function::{self, Class, FormalParameterList, Function},
operations::{contains, contains_arguments, has_direct_super, ContainsSymbol},
property::{ClassElementName, MethodDefinition},
function::{
self, ClassDeclaration as ClassDeclarationNode, ClassElementName, ClassMethodDefinition,
FormalParameterList, FunctionExpression,
},
operations::{contains, contains_arguments, ContainsSymbol},
Expression, Keyword, Punctuator,
};
use boa_interner::{Interner, Sym};
@ -68,19 +71,17 @@ impl<R> TokenParser<R> for ClassDeclaration
where
R: ReadChar,
{
type Output = Class;
type Output = ClassDeclarationNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect((Keyword::Class, false), "class declaration", interner)?;
let strict = cursor.strict();
cursor.set_strict(true);
let mut has_binding_identifier = false;
let token = cursor.peek(0, interner).or_abrupt()?;
let name = match token.kind() {
TokenKind::IdentifierName(_)
| TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {
has_binding_identifier = true;
BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?
}
@ -95,13 +96,15 @@ where
};
cursor.set_strict(strict);
ClassTail::new(
let (super_ref, constructor, elements) =
ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?;
Ok(ClassDeclarationNode::new(
name,
has_binding_identifier,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)
super_ref,
constructor,
elements.into_boxed_slice(),
))
}
}
@ -114,19 +117,13 @@ where
#[derive(Debug, Clone, Copy)]
pub(in crate::parser) struct ClassTail {
name: Option<Identifier>,
has_binding_identifier: bool,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ClassTail {
/// Creates a new `ClassTail` parser.
pub(in crate::parser) fn new<N, Y, A>(
name: N,
has_binding_identifier: bool,
allow_yield: Y,
allow_await: A,
) -> Self
pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
@ -134,7 +131,6 @@ impl ClassTail {
{
Self {
name: name.into(),
has_binding_identifier,
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
@ -145,7 +141,11 @@ impl<R> TokenParser<R> for ClassTail
where
R: ReadChar,
{
type Output = Class;
type Output = (
Option<Expression>,
Option<FunctionExpression>,
Vec<function::ClassElement>,
);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let token = cursor.peek(0, interner).or_abrupt()?;
@ -173,13 +173,7 @@ where
if is_close_block {
cursor.advance(interner);
Ok(Class::new(
self.name,
super_ref,
None,
Box::default(),
self.has_binding_identifier,
))
Ok((super_ref, None, Vec::new()))
} else {
let body_start = cursor.peek(0, interner).or_abrupt()?.span().start();
let (constructor, elements) =
@ -198,13 +192,7 @@ where
}
}
Ok(Class::new(
self.name,
super_ref,
constructor,
elements.into(),
self.has_binding_identifier,
))
Ok((super_ref, constructor, elements))
}
}
}
@ -250,7 +238,7 @@ where
let strict = cursor.strict();
cursor.set_strict(true);
let lhs = LeftHandSideExpression::new(None, self.allow_yield, self.allow_await)
let lhs = LeftHandSideExpression::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict(strict);
@ -291,7 +279,7 @@ impl<R> TokenParser<R> for ClassBody
where
R: ReadChar,
{
type Output = (Option<Function>, Vec<function::ClassElement>);
type Output = (Option<FunctionExpression>, Vec<function::ClassElement>);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let mut constructor = None;
@ -305,184 +293,116 @@ where
loop {
let token = cursor.peek(0, interner).or_abrupt()?;
let position = token.span().start();
match token.kind() {
let (parsed_constructor, element) = match token.kind() {
TokenKind::Punctuator(Punctuator::CloseBlock) => break,
_ => match ClassElement::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?
{
(Some(_), None) if constructor.is_some() => {
return Err(Error::general(
"a class may only have one constructor",
position,
));
_ => ClassElement::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
};
if let Some(c) = parsed_constructor {
if constructor.is_some() {
return Err(Error::general(
"a class may only have one constructor",
position,
));
}
constructor = Some(c);
}
let Some(element) = element else {
continue;
};
match &element {
function::ClassElement::MethodDefinition(m) => {
// It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true.
if let ClassElementName::PropertyName(name) = m.name() {
if contains(name, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
}
(Some(c), None) => {
constructor = Some(c);
if contains(m.parameters(), ContainsSymbol::SuperCall)
|| contains(m.body(), ContainsSymbol::SuperCall)
{
return Err(Error::lex(LexError::Syntax(
"invalid super call usage".into(),
position,
)));
}
(None, Some(element)) => {
match &element {
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) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
match method {
MethodDefinition::Get(_) => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::Setter) => {
private_elements_names.insert(
name.description(),
PrivateElement::Value,
);
}
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
None => {
private_elements_names.insert(
name.description(),
PrivateElement::Getter,
);
}
}
if let ClassElementName::PrivateName(name) = m.name() {
match m.kind() {
MethodDefinitionKind::Get => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::StaticSetter) if m.is_static() => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticValue,
);
}
Some(PrivateElement::Setter) if !m.is_static() => {
private_elements_names
.insert(name.description(), PrivateElement::Value);
}
MethodDefinition::Set(_) => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::Getter) => {
private_elements_names.insert(
name.description(),
PrivateElement::Value,
);
}
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
None => {
private_elements_names.insert(
name.description(),
PrivateElement::Setter,
);
}
}
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
_ => {
if private_elements_names
.insert(name.description(), PrivateElement::Value)
.is_some()
{
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
None => {
private_elements_names.insert(
name.description(),
if m.is_static() {
PrivateElement::StaticGetter
} else {
PrivateElement::Getter
},
);
}
}
}
function::ClassElement::PrivateStaticMethodDefinition(name, method) => {
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
match method {
MethodDefinition::Get(_) => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::StaticSetter) => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticValue,
);
}
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
None => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticGetter,
);
}
}
}
MethodDefinition::Set(_) => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::StaticGetter) => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticValue,
);
}
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
None => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticSetter,
);
}
}
MethodDefinitionKind::Set => {
match private_elements_names.get(&name.description()) {
Some(PrivateElement::StaticGetter) if m.is_static() => {
private_elements_names.insert(
name.description(),
PrivateElement::StaticValue,
);
}
_ => {
if private_elements_names
.insert(name.description(), PrivateElement::StaticValue)
.is_some()
{
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
Some(PrivateElement::Getter) if !m.is_static() => {
private_elements_names
.insert(name.description(), PrivateElement::Value);
}
}
}
function::ClassElement::PrivateFieldDefinition(name, init) => {
if let Some(node) = init {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
Some(_) => {
return Err(Error::general(
"private identifier has already been declared",
position,
)));
));
}
}
if private_elements_names
.insert(name.description(), PrivateElement::Value)
.is_some()
{
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
}
function::ClassElement::PrivateStaticFieldDefinition(name, init) => {
if let Some(node) = init {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
None => {
private_elements_names.insert(
name.description(),
if m.is_static() {
PrivateElement::StaticSetter
} else {
PrivateElement::Setter
},
);
}
}
}
_ => {
if private_elements_names
.insert(name.description(), PrivateElement::StaticValue)
.insert(
name.description(),
if m.is_static() {
PrivateElement::StaticValue
} else {
PrivateElement::Value
},
)
.is_some()
{
return Err(Error::general(
@ -491,35 +411,59 @@ where
));
}
}
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:
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(method) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
}
function::ClassElement::FieldDefinition(_, Some(node))
| function::ClassElement::StaticFieldDefinition(_, Some(node)) => {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
}
_ => {}
}
elements.push(element);
}
_ => {}
},
}
function::ClassElement::PrivateFieldDefinition(name, init) => {
if let Some(node) = init {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
}
if private_elements_names
.insert(name.description(), PrivateElement::Value)
.is_some()
{
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
}
function::ClassElement::PrivateStaticFieldDefinition(name, init) => {
if let Some(node) = init {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
}
if private_elements_names
.insert(name.description(), PrivateElement::StaticValue)
.is_some()
{
return Err(Error::general(
"private identifier has already been declared",
position,
));
}
}
function::ClassElement::FieldDefinition(_, Some(node))
| function::ClassElement::StaticFieldDefinition(_, Some(node)) => {
if contains(node, ContainsSymbol::SuperCall) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
position,
)));
}
}
_ => {}
}
elements.push(element);
}
cursor.set_strict(strict);
@ -572,7 +516,7 @@ impl<R> TokenParser<R> for ClassElement
where
R: ReadChar,
{
type Output = (Option<Function>, Option<function::ClassElement>);
type Output = (Option<FunctionExpression>, Option<function::ClassElement>);
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let token = cursor.peek(0, interner).or_abrupt()?;
@ -643,7 +587,10 @@ where
)?;
cursor.set_strict(strict);
return Ok((Some(Function::new(self.name, parameters, body)), None));
return Ok((
Some(FunctionExpression::new(self.name, parameters, body, false)),
None,
));
}
TokenKind::Punctuator(Punctuator::OpenBlock) if r#static => {
cursor.advance(interner);
@ -743,39 +690,38 @@ where
}
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
let (class_element_name, params, body) =
GeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
if property_name.literal() == Some(Sym::PROTOTYPE) {
let name = match class_element_name {
ClassElementName::PropertyName(name) => {
if r#static && name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::StaticMethodDefinition(property_name, method)
}
ClassElementName::PropertyName(property_name) => {
function::ClassElement::MethodDefinition(property_name, method)
ClassElementName::PropertyName(name)
}
ClassElementName::PrivateIdentifier(name)
if name.description() == Sym::CONSTRUCTOR =>
{
return Err(Error::general(
"class constructor may not be a private method",
name_position,
))
}
ClassElementName::PrivateIdentifier(private_ident) if r#static => {
function::ClassElement::PrivateStaticMethodDefinition(private_ident, method)
}
ClassElementName::PrivateIdentifier(private_ident) => {
function::ClassElement::PrivateMethodDefinition(private_ident, method)
ClassElementName::PrivateName(name) => {
if name.description() == Sym::CONSTRUCTOR {
return Err(Error::general(
"class constructor may not be a private method",
name_position,
));
}
ClassElementName::PrivateName(name)
}
}
};
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
name,
params,
body,
MethodDefinitionKind::Generator,
r#static,
))
}
TokenKind::Keyword((Keyword::Async, true)) if is_keyword => {
return Err(Error::general(
@ -808,39 +754,33 @@ where
}
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
let (class_element_name, params, body) =
AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
if property_name.literal() == Some(Sym::PROTOTYPE) {
let name = match class_element_name {
ClassElementName::PropertyName(name) => {
if r#static && name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::StaticMethodDefinition(
property_name,
method,
)
}
ClassElementName::PropertyName(property_name) => {
function::ClassElement::MethodDefinition(property_name, method)
ClassElementName::PropertyName(name)
}
ClassElementName::PrivateIdentifier(private_ident) if r#static => {
function::ClassElement::PrivateStaticMethodDefinition(
private_ident,
method,
)
ClassElementName::PrivateName(name) => {
ClassElementName::PrivateName(name)
}
ClassElementName::PrivateIdentifier(private_ident) => {
function::ClassElement::PrivateMethodDefinition(
private_ident,
method,
)
}
}
};
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
name,
params,
body,
MethodDefinitionKind::AsyncGenerator,
r#static,
))
}
TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general(
@ -852,44 +792,38 @@ where
let name_position = token.span().start();
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
let (class_element_name, params, body) =
AsyncMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
if property_name.literal() == Some(Sym::PROTOTYPE) {
let name = match class_element_name {
ClassElementName::PropertyName(name) => {
if r#static && name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::StaticMethodDefinition(
property_name,
method,
)
}
ClassElementName::PropertyName(property_name) => {
function::ClassElement::MethodDefinition(property_name, method)
ClassElementName::PropertyName(name)
}
ClassElementName::PrivateIdentifier(name)
if name.description() == Sym::CONSTRUCTOR && r#static =>
{
return Err(Error::general(
"class constructor may not be a private method",
name_position,
))
}
ClassElementName::PrivateIdentifier(identifier) if r#static => {
function::ClassElement::PrivateStaticMethodDefinition(
identifier, method,
)
}
ClassElementName::PrivateIdentifier(identifier) => {
function::ClassElement::PrivateMethodDefinition(identifier, method)
ClassElementName::PrivateName(name) => {
if r#static && name.description() == Sym::CONSTRUCTOR {
return Err(Error::general(
"class constructor may not be a private method",
name_position,
));
}
ClassElementName::PrivateName(name)
}
}
};
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
name,
params,
body,
MethodDefinitionKind::Async,
r#static,
))
}
}
}
@ -940,18 +874,13 @@ where
)));
}
cursor.set_strict(strict);
let method = MethodDefinition::Get(Function::new(None, params, body));
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
PrivateName::new(name),
method,
)
} else {
function::ClassElement::PrivateMethodDefinition(
PrivateName::new(name),
method,
)
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PrivateName(PrivateName::new(name)),
params,
body,
MethodDefinitionKind::Get,
r#static,
))
}
TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general(
@ -992,23 +921,19 @@ where
"class getter",
interner,
)?;
let method = MethodDefinition::Get(Function::new(
None,
if r#static && name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PropertyName(name),
FormalParameterList::default(),
body,
));
if r#static {
if name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::StaticMethodDefinition(name, method)
} else {
function::ClassElement::MethodDefinition(name, method)
}
MethodDefinitionKind::Get,
r#static,
))
}
_ => {
cursor.expect_semicolon("expected semicolon", interner)?;
@ -1065,18 +990,13 @@ where
)));
}
cursor.set_strict(strict);
let method = MethodDefinition::Set(Function::new(None, params, body));
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
PrivateName::new(name),
method,
)
} else {
function::ClassElement::PrivateMethodDefinition(
PrivateName::new(name),
method,
)
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PrivateName(PrivateName::new(name)),
params,
body,
MethodDefinitionKind::Set,
r#static,
))
}
TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general(
@ -1119,18 +1039,19 @@ where
)));
}
cursor.set_strict(strict);
let method = MethodDefinition::Set(Function::new(None, params, body));
if r#static {
if name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::StaticMethodDefinition(name, method)
} else {
function::ClassElement::MethodDefinition(name, method)
if r#static && name.literal() == Some(Sym::PROTOTYPE) {
return Err(Error::general(
"class may not have static method definitions named 'prototype'",
name_position,
));
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PropertyName(name),
params,
body,
MethodDefinitionKind::Set,
r#static,
))
}
_ => {
cursor.expect_semicolon("expected semicolon", interner)?;
@ -1156,11 +1077,6 @@ where
}
TokenKind::PrivateIdentifier(name) => {
let name = *name;
let name_private = interner.get_or_intern(
[utf16!("#"), interner.resolve_expect(name).utf16()]
.concat()
.as_slice(),
);
cursor.advance(interner);
let token = cursor.peek(0, interner).or_abrupt()?;
match token.kind() {
@ -1168,15 +1084,17 @@ where
cursor.advance(interner);
let strict = cursor.strict();
cursor.set_strict(true);
let rhs = AssignmentExpression::new(
Some(name_private.into()),
true,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let mut rhs =
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect_semicolon("expected semicolon", interner)?;
cursor.set_strict(strict);
let function_name = interner.get_or_intern(
[utf16!("#"), interner.resolve_expect(name).utf16()]
.concat()
.as_slice(),
);
rhs.set_anonymous_function_definition_name(&Identifier::new(function_name));
if r#static {
function::ClassElement::PrivateStaticFieldDefinition(
PrivateName::new(name),
@ -1210,24 +1128,18 @@ where
// and IsSimpleParameterList of UniqueFormalParameters is false.
if body.strict() && !params.is_simple() {
return Err(Error::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
token.span().start(),
)));
)));
}
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
cursor.set_strict(strict);
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
PrivateName::new(name),
method,
)
} else {
function::ClassElement::PrivateMethodDefinition(
PrivateName::new(name),
method,
)
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PrivateName(PrivateName::new(name)),
params,
body,
MethodDefinitionKind::Ordinary,
r#static,
))
}
_ => {
cursor.expect_semicolon("expected semicolon", interner)?;
@ -1275,15 +1187,14 @@ where
cursor.advance(interner);
let strict = cursor.strict();
cursor.set_strict(true);
let rhs = AssignmentExpression::new(
name.literal().map(Into::into),
true,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let mut rhs =
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect_semicolon("expected semicolon", interner)?;
cursor.set_strict(strict);
if let Some(name) = name.literal() {
rhs.set_anonymous_function_definition_name(&Identifier::new(name));
}
if r#static {
function::ClassElement::StaticFieldDefinition(name, Some(rhs))
} else {
@ -1322,13 +1233,14 @@ where
token.span().start(),
)));
}
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
cursor.set_strict(strict);
if r#static {
function::ClassElement::StaticMethodDefinition(name, method)
} else {
function::ClassElement::MethodDefinition(name, method)
}
function::ClassElement::MethodDefinition(ClassMethodDefinition::new(
ClassElementName::PropertyName(name),
params,
body,
MethodDefinitionKind::Ordinary,
r#static,
))
}
_ => {
if let Some(name) = name.literal() {

45
core/parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs

@ -6,8 +6,11 @@ use boa_ast::{
literal::Literal,
Call, Identifier,
},
function::{Class, ClassElement, FormalParameterList, Function, FunctionBody},
property::{MethodDefinition, PropertyName},
function::{
ClassDeclaration, ClassElement, ClassMethodDefinition, FormalParameterList, FunctionBody,
FunctionExpression,
},
property::{MethodDefinitionKind, PropertyName},
Declaration, Expression, Statement, StatementList, StatementListItem,
};
use boa_interner::Interner;
@ -17,26 +20,26 @@ use boa_macros::utf16;
fn check_async_ordinary_method() {
let interner = &mut Interner::default();
let elements = vec![ClassElement::MethodDefinition(
PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))),
MethodDefinition::Ordinary(Function::new(
None,
FormalParameterList::default(),
FunctionBody::default(),
let elements = vec![ClassElement::MethodDefinition(ClassMethodDefinition::new(
boa_ast::function::ClassElementName::PropertyName(PropertyName::Literal(
interner.get_or_intern_static("async", utf16!("async")),
)),
)];
FormalParameterList::default(),
FunctionBody::default(),
MethodDefinitionKind::Ordinary,
false,
))];
check_script_parser(
"class A {
async() { }
}
",
[Declaration::Class(Class::new(
Some(interner.get_or_intern_static("A", utf16!("A")).into()),
[Declaration::ClassDeclaration(ClassDeclaration::new(
interner.get_or_intern_static("A", utf16!("A")).into(),
None,
None,
elements.into(),
true,
))
.into()],
interner,
@ -58,12 +61,11 @@ fn check_async_field_initialization() {
= 1
}
",
[Declaration::Class(Class::new(
Some(interner.get_or_intern_static("A", utf16!("A")).into()),
[Declaration::ClassDeclaration(ClassDeclaration::new(
interner.get_or_intern_static("A", utf16!("A")).into(),
None,
None,
elements.into(),
true,
))
.into()],
interner,
@ -84,12 +86,11 @@ fn check_async_field() {
async
}
",
[Declaration::Class(Class::new(
Some(interner.get_or_intern_static("A", utf16!("A")).into()),
[Declaration::ClassDeclaration(ClassDeclaration::new(
interner.get_or_intern_static("A", utf16!("A")).into(),
None,
None,
elements.into(),
true,
))
.into()],
interner,
@ -117,21 +118,21 @@ fn check_new_target_with_property_access() {
[new_target].into(),
));
let constructor = Function::new(
let constructor = FunctionExpression::new(
Some(interner.get_or_intern_static("A", utf16!("A")).into()),
FormalParameterList::default(),
FunctionBody::new(StatementList::new(
[Statement::Expression(console).into()],
false,
)),
false,
);
let class = Class::new(
Some(interner.get("A").unwrap().into()),
let class = ClassDeclaration::new(
interner.get("A").unwrap().into(),
None,
Some(constructor),
Box::default(),
true,
);
let instantiation = Expression::New(

11
core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs

@ -8,7 +8,7 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{function::Function, Keyword};
use boa_ast::{function::FunctionDeclaration as FunctionDeclarationNode, Keyword};
use boa_interner::Interner;
/// Function declaration parsing.
@ -74,18 +74,13 @@ impl<R> TokenParser<R> for FunctionDeclaration
where
R: ReadChar,
{
type Output = Function;
type Output = FunctionDeclarationNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect((Keyword::Function, false), "function declaration", interner)?;
let result = parse_callable_declaration(&self, cursor, interner)?;
Ok(Function::new_with_binding_identifier(
Some(result.0),
result.1,
result.2,
true,
))
Ok(FunctionDeclarationNode::new(result.0, result.1, result.2))
}
}

24
core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs

@ -1,6 +1,6 @@
use crate::parser::tests::check_script_parser;
use boa_ast::{
function::{FormalParameterList, Function, FunctionBody},
function::{FormalParameterList, FunctionBody, FunctionDeclaration},
Declaration,
};
use boa_interner::Interner;
@ -12,15 +12,12 @@ fn function_declaration() {
let interner = &mut Interner::default();
check_script_parser(
"function hello() {}",
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(
interner
.get_or_intern_static("hello", utf16!("hello"))
.into(),
),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
interner
.get_or_intern_static("hello", utf16!("hello"))
.into(),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
interner,
@ -32,15 +29,12 @@ fn function_declaration() {
fn function_declaration_keywords() {
macro_rules! genast {
($keyword:literal, $interner:expr) => {
vec![Declaration::Function(Function::new_with_binding_identifier(
Some(
$interner
.get_or_intern_static($keyword, utf16!($keyword))
.into(),
),
vec![Declaration::FunctionDeclaration(FunctionDeclaration::new(
$interner
.get_or_intern_static($keyword, utf16!($keyword))
.into(),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()]
};

6
core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs

@ -8,7 +8,7 @@ use crate::{
},
source::ReadChar,
};
use boa_ast::{function::Generator, Keyword, Punctuator};
use boa_ast::{function::GeneratorDeclaration as GeneratorDeclarationNode, Keyword, Punctuator};
use boa_interner::Interner;
/// Generator declaration parsing.
@ -76,7 +76,7 @@ impl<R> TokenParser<R> for GeneratorDeclaration
where
R: ReadChar,
{
type Output = Generator;
type Output = GeneratorDeclarationNode;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect(
@ -88,6 +88,6 @@ where
let result = parse_callable_declaration(&self, cursor, interner)?;
Ok(Generator::new(Some(result.0), result.1, result.2, true))
Ok(GeneratorDeclarationNode::new(result.0, result.1, result.2))
}
}

7
core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs

@ -1,6 +1,6 @@
use crate::parser::tests::check_script_parser;
use boa_ast::{
function::{FormalParameterList, FunctionBody, Generator},
function::{FormalParameterList, FunctionBody, GeneratorDeclaration},
Declaration,
};
use boa_interner::Interner;
@ -11,11 +11,10 @@ fn generator_function_declaration() {
let interner = &mut Interner::default();
check_script_parser(
"function* gen() {}",
vec![Declaration::Generator(Generator::new(
Some(interner.get_or_intern_static("gen", utf16!("gen")).into()),
vec![Declaration::GeneratorDeclaration(GeneratorDeclaration::new(
interner.get_or_intern_static("gen", utf16!("gen")).into(),
FormalParameterList::default(),
FunctionBody::default(),
true,
))
.into()],
interner,

18
core/parser/src/parser/statement/declaration/lexical.rs

@ -312,7 +312,7 @@ where
.is_some()
{
Some(
Initializer::new(None, self.allow_in, self.allow_yield, self.allow_await)
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -340,7 +340,7 @@ where
.is_some()
{
Some(
Initializer::new(None, self.allow_in, self.allow_yield, self.allow_await)
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -374,15 +374,11 @@ where
.filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))
.is_some()
{
Some(
Initializer::new(
Some(ident),
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?,
)
let mut init =
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
init.set_anonymous_function_definition_name(&ident);
Some(init)
} else {
None
};

4
core/parser/src/parser/statement/expression/mod.rs

@ -100,8 +100,8 @@ where
_ => {}
}
let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expr =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect_semicolon("expression statement", interner)?;

16
core/parser/src/parser/statement/if_stm/mod.rs

@ -63,8 +63,8 @@ where
cursor.expect((Keyword::If, false), "if statement", interner)?;
cursor.expect(Punctuator::OpenParen, "if statement", interner)?;
let condition = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let condition =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
let position = cursor
.expect(Punctuator::CloseParen, "if statement", interner)?
@ -83,10 +83,12 @@ where
// Source text matched by this production is processed as if each matching
// occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
// StatementListItem of a BlockStatement occupying that position in the source text.
Block::from(vec![StatementListItem::Declaration(Declaration::Function(
FunctionDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor, interner)?,
))])
Block::from(vec![StatementListItem::Declaration(
Declaration::FunctionDeclaration(
FunctionDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor, interner)?,
),
)])
.into()
}
_ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)
@ -126,7 +128,7 @@ where
// occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
// StatementListItem of a BlockStatement occupying that position in the source text.
Block::from(vec![StatementListItem::Declaration(
Declaration::Function(
Declaration::FunctionDeclaration(
FunctionDeclaration::new(
self.allow_yield,
self.allow_await,

4
core/parser/src/parser/statement/iteration/do_while_statement.rs

@ -99,8 +99,8 @@ where
cursor.expect(Punctuator::OpenParen, "do while statement", interner)?;
let cond = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let cond =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "do while statement", interner)?;

12
core/parser/src/parser/statement/iteration/for_statement.rs

@ -144,14 +144,14 @@ where
}
Some(
Expression::new(None, false, self.allow_yield, self.allow_await)
Expression::new(false, self.allow_yield, self.allow_await)
.parse(cursor, interner)?
.into(),
)
}
TokenKind::Punctuator(Punctuator::Semicolon) => None,
_ => Some(
Expression::new(None, false, self.allow_yield, self.allow_await)
Expression::new(false, self.allow_yield, self.allow_await)
.parse(cursor, interner)?
.into(),
),
@ -203,10 +203,10 @@ where
cursor.advance(interner);
let expr = if in_loop {
Expression::new(None, true, self.allow_yield, self.allow_await)
Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?
} else {
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
AssignmentExpression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?
};
@ -281,7 +281,7 @@ where
let cond = if cursor.next_if(Punctuator::Semicolon, interner)?.is_some() {
None
} else {
let step = Expression::new(None, true, self.allow_yield, self.allow_await)
let step = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::Semicolon, "for statement", interner)?;
Some(step)
@ -290,7 +290,7 @@ where
let step = if cursor.next_if(Punctuator::CloseParen, interner)?.is_some() {
None
} else {
let step = Expression::new(None, true, self.allow_yield, self.allow_await)
let step = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseParen),

4
core/parser/src/parser/statement/iteration/while_statement.rs

@ -57,8 +57,8 @@ where
cursor.expect(Punctuator::OpenParen, "while statement", interner)?;
let cond = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let cond =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "while statement", interner)?;

34
core/parser/src/parser/statement/mod.rs

@ -559,7 +559,6 @@ where
match peek_token.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let init = Initializer::new(
None,
true,
self.allow_yield,
self.allow_await,
@ -592,7 +591,6 @@ where
match peek_token.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let init = Initializer::new(
None,
true,
self.allow_yield,
self.allow_await,
@ -626,13 +624,13 @@ where
if let Some(peek_token) = cursor.peek(0, interner)? {
match peek_token.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let init = Initializer::new(
None,
let mut init = Initializer::new(
true,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
init.set_anonymous_function_definition_name(&ident);
patterns.push(ObjectPatternElement::SingleName {
ident,
name: property_name,
@ -656,13 +654,10 @@ where
.parse(cursor, interner)?;
match cursor.peek(0, interner)?.map(Token::kind) {
Some(TokenKind::Punctuator(Punctuator::Assign)) => {
let init = Initializer::new(
Some(name),
true,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let mut init =
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
init.set_anonymous_function_definition_name(&name);
patterns.push(ObjectPatternElement::SingleName {
ident: name,
name: name.sym().into(),
@ -811,7 +806,7 @@ where
match cursor.peek(0, interner).or_abrupt()?.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let default_init =
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
patterns.push(ArrayPatternElement::Pattern {
pattern: bindings.into(),
@ -835,7 +830,7 @@ where
match cursor.peek(0, interner).or_abrupt()?.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let default_init =
Initializer::new(None, true, self.allow_yield, self.allow_await)
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
patterns.push(ArrayPatternElement::Pattern {
pattern: bindings.into(),
@ -857,16 +852,13 @@ where
.parse(cursor, interner)?;
match cursor.peek(0, interner).or_abrupt()?.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
let default_init = Initializer::new(
Some(ident),
true,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?;
let mut init =
Initializer::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
init.set_anonymous_function_definition_name(&ident);
patterns.push(ArrayPatternElement::SingleName {
ident,
default_init: Some(default_init),
default_init: Some(init),
});
}
_ => {

4
core/parser/src/parser/statement/return_stm/mod.rs

@ -57,8 +57,8 @@ where
return Ok(Return::new(None));
}
let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expr =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect_semicolon("return statement", interner)?;

6
core/parser/src/parser/statement/switch/mod.rs

@ -65,8 +65,8 @@ where
cursor.expect((Keyword::Switch, false), "switch statement", interner)?;
cursor.expect(Punctuator::OpenParen, "switch statement", interner)?;
let condition = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let condition =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect(Punctuator::CloseParen, "switch statement", interner)?;
@ -162,7 +162,7 @@ where
}
TokenKind::Keyword((Keyword::Case, false)) => {
// Case statement.
let cond = Expression::new(None, true, self.allow_yield, self.allow_await)
let cond = Expression::new(true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::Colon, "switch case block", interner)?;

4
core/parser/src/parser/statement/throw/mod.rs

@ -49,8 +49,8 @@ where
cursor.peek_expect_no_lineterminator(0, "throw statement", interner)?;
let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expr =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect_semicolon("throw statement", interner)?;

18
core/parser/src/parser/statement/variable/mod.rs

@ -177,7 +177,7 @@ where
.is_some()
{
Some(
Initializer::new(None, self.allow_in, self.allow_yield, self.allow_await)
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -196,7 +196,7 @@ where
.is_some()
{
Some(
Initializer::new(None, self.allow_in, self.allow_yield, self.allow_await)
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
} else {
@ -214,15 +214,11 @@ where
.filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))
.is_some()
{
Some(
Initializer::new(
Some(ident),
self.allow_in,
self.allow_yield,
self.allow_await,
)
.parse(cursor, interner)?,
)
let mut init =
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
init.set_anonymous_function_definition_name(&ident);
Some(init)
} else {
None
};

4
core/parser/src/parser/statement/with/mod.rs

@ -70,8 +70,8 @@ where
}
cursor.expect(Punctuator::OpenParen, "with statement", interner)?;
let expression = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let expression =
Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;
let position = cursor
.expect(Punctuator::CloseParen, "with statement", interner)?
.span()

12
core/parser/src/parser/tests/mod.rs

@ -9,7 +9,7 @@ use boa_ast::{
declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable},
expression::{
access::SimplePropertyAccess,
literal::{Literal, ObjectLiteral},
literal::{Literal, ObjectLiteral, PropertyDefinition},
operator::{
assign::AssignOp,
binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp},
@ -19,10 +19,9 @@ use boa_ast::{
Call, Identifier, New, Parenthesized,
},
function::{
ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function,
FunctionBody,
ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags,
FunctionBody, FunctionDeclaration,
},
property::PropertyDefinition,
statement::{If, Return},
Expression, Script, Statement, StatementList, StatementListItem,
};
@ -123,14 +122,13 @@ fn hoisting() {
UpdateTarget::Identifier(Identifier::new(a)),
)))
.into(),
Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()),
Declaration::FunctionDeclaration(FunctionDeclaration::new(
hello.into(),
FormalParameterList::default(),
FunctionBody::new(
vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()]
.into(),
),
true,
))
.into(),
],

Loading…
Cancel
Save