diff --git a/boa_ast/src/expression/access.rs b/boa_ast/src/expression/access.rs index b3b8abd6258..fd189badfd8 100644 --- a/boa_ast/src/expression/access.rs +++ b/boa_ast/src/expression/access.rs @@ -15,6 +15,7 @@ //! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors use crate::expression::Expression; +use crate::function::PrivateName; use crate::try_break; use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; @@ -215,14 +216,14 @@ impl VisitWith for SimplePropertyAccess { #[derive(Clone, Debug, PartialEq)] pub struct PrivatePropertyAccess { target: Box, - field: Sym, + field: PrivateName, } impl PrivatePropertyAccess { /// Creates a `GetPrivateField` AST Expression. #[inline] #[must_use] - pub fn new(value: Expression, field: Sym) -> Self { + pub fn new(value: Expression, field: PrivateName) -> Self { Self { target: value.into(), field, @@ -239,7 +240,7 @@ impl PrivatePropertyAccess { /// Gets the name of the field to retrieve. #[inline] #[must_use] - pub const fn field(&self) -> Sym { + pub const fn field(&self) -> PrivateName { self.field } } @@ -250,7 +251,7 @@ impl ToInternedString for PrivatePropertyAccess { format!( "{}.#{}", self.target.to_interned_string(interner), - interner.resolve_expect(self.field) + interner.resolve_expect(self.field.description()) ) } } @@ -268,7 +269,7 @@ impl VisitWith for PrivatePropertyAccess { V: Visitor<'a>, { try_break!(visitor.visit_expression(&self.target)); - visitor.visit_sym(&self.field) + visitor.visit_private_name(&self.field) } fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow @@ -276,7 +277,7 @@ impl VisitWith for PrivatePropertyAccess { V: VisitorMut<'a>, { try_break!(visitor.visit_expression_mut(&mut self.target)); - visitor.visit_sym_mut(&mut self.field) + visitor.visit_private_name_mut(&mut self.field) } } diff --git a/boa_ast/src/expression/mod.rs b/boa_ast/src/expression/mod.rs index 23087ba7629..282006ed680 100644 --- a/boa_ast/src/expression/mod.rs +++ b/boa_ast/src/expression/mod.rs @@ -216,6 +216,27 @@ impl Expression { | Self::Class(_) ) } + + /// Returns if the expression is a function definition without a name. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition + #[must_use] + #[inline] + pub const fn is_anonymous_function_definition(&self) -> bool { + 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(), + _ => false, + } + } } impl From for Statement { diff --git a/boa_ast/src/expression/optional.rs b/boa_ast/src/expression/optional.rs index b7d16b3ea91..36f4e44b8d8 100644 --- a/boa_ast/src/expression/optional.rs +++ b/boa_ast/src/expression/optional.rs @@ -1,11 +1,11 @@ -use boa_interner::{Interner, Sym, ToInternedString}; -use core::ops::ControlFlow; - -use crate::join_nodes; -use crate::try_break; -use crate::visitor::{VisitWith, Visitor, VisitorMut}; - use super::{access::PropertyAccessField, Expression}; +use crate::{ + function::PrivateName, + join_nodes, try_break, + visitor::{VisitWith, Visitor, VisitorMut}, +}; +use boa_interner::{Interner, ToInternedString}; +use core::ops::ControlFlow; /// List of valid operations in an [`Optional`] chain. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -20,7 +20,7 @@ pub enum OptionalOperationKind { /// A private property access (`a?.#prop`). PrivatePropertyAccess { /// The private property accessed. - field: Sym, + field: PrivateName, }, /// A function call (`a?.(arg)`). Call { @@ -36,7 +36,7 @@ impl VisitWith for OptionalOperationKind { { match self { Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field), - Self::PrivatePropertyAccess { field } => visitor.visit_sym(field), + Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field), Self::Call { args } => { for arg in args.iter() { try_break!(visitor.visit_expression(arg)); @@ -52,7 +52,7 @@ impl VisitWith for OptionalOperationKind { { match self { Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field), - Self::PrivatePropertyAccess { field } => visitor.visit_sym_mut(field), + Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field), Self::Call { args } => { for arg in args.iter_mut() { try_break!(visitor.visit_expression_mut(arg)); @@ -113,7 +113,7 @@ impl ToInternedString for OptionalOperation { } if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind { - return format!(".#{}", interner.resolve_expect(*field)); + return format!(".#{}", interner.resolve_expect(field.description())); } String::new() @@ -126,7 +126,7 @@ impl ToInternedString for OptionalOperation { } }, OptionalOperationKind::PrivatePropertyAccess { field } => { - format!("#{}", interner.resolve_expect(*field)) + format!("#{}", interner.resolve_expect(field.description())) } OptionalOperationKind::Call { args } => format!("({})", join_nodes(interner, args)), }); diff --git a/boa_ast/src/function/class.rs b/boa_ast/src/function/class.rs index 1106371932d..f12afa8b660 100644 --- a/boa_ast/src/function/class.rs +++ b/boa_ast/src/function/class.rs @@ -1,6 +1,4 @@ -use core::ops::ControlFlow; -use std::borrow::Cow; - +use super::Function; use crate::{ block_to_string, expression::{Expression, Identifier}, @@ -11,8 +9,9 @@ use crate::{ Declaration, StatementList, ToStringEscaped, }; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; - -use super::Function; +use core::ops::ControlFlow; +use std::borrow::Cow; +use std::hash::Hash; /// A class declaration, as defined by the [spec]. /// @@ -27,8 +26,9 @@ use super::Function; pub struct Class { name: Option, super_ref: Option, - constructor: Option, - elements: Box<[ClassElement]>, + pub(crate) constructor: Option, + pub(crate) elements: Box<[ClassElement]>, + has_binding_identifier: bool, } impl Class { @@ -40,12 +40,14 @@ impl Class { super_ref: Option, constructor: Option, elements: Box<[ClassElement]>, + has_binding_identifier: bool, ) -> Self { Self { name, super_ref, constructor, elements, + has_binding_identifier, } } @@ -76,6 +78,13 @@ impl Class { pub const fn elements(&self) -> &[ClassElement] { &self.elements } + + /// Returns whether the class has a binding identifier. + #[inline] + #[must_use] + pub const fn has_binding_identifier(&self) -> bool { + self.has_binding_identifier + } } impl ToIndentedString for Class { @@ -238,7 +247,7 @@ impl ToIndentedString for Class { MethodDefinition::Set(_) => "set ", _ => "", }, - interner.resolve_expect(*name), + interner.resolve_expect(name.description()), match &method { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) @@ -281,7 +290,7 @@ impl ToIndentedString for Class { MethodDefinition::Set(_) => "set ", _ => "", }, - interner.resolve_expect(*name), + interner.resolve_expect(name.description()), match &method { MethodDefinition::Get(expr) | MethodDefinition::Set(expr) @@ -320,24 +329,30 @@ impl ToIndentedString for Class { Some(expr) => { format!( "{indentation}#{} = {};\n", - interner.resolve_expect(*name), + interner.resolve_expect(name.description()), expr.to_no_indent_string(interner, indent_n + 1) ) } None => { - format!("{indentation}#{};\n", interner.resolve_expect(*name),) + format!( + "{indentation}#{};\n", + interner.resolve_expect(name.description()), + ) } }, ClassElement::PrivateStaticFieldDefinition(name, field) => match field { Some(expr) => { format!( "{indentation}static #{} = {};\n", - interner.resolve_expect(*name), + interner.resolve_expect(name.description()), expr.to_no_indent_string(interner, indent_n + 1) ) } None => { - format!("{indentation}static #{};\n", interner.resolve_expect(*name),) + format!( + "{indentation}static #{};\n", + interner.resolve_expect(name.description()), + ) } }, ClassElement::StaticBlock(statement_list) => { @@ -414,22 +429,30 @@ impl VisitWith for Class { 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 field definition. FieldDefinition(PropertyName, Option), + /// A static field definition, accessible from the class constructor object StaticFieldDefinition(PropertyName, Option), + /// A private method definition, only accessible inside the class declaration. - PrivateMethodDefinition(Sym, MethodDefinition), + PrivateMethodDefinition(PrivateName, MethodDefinition), + /// A private static method definition, only accessible from static methods and fields inside /// the class declaration. - PrivateStaticMethodDefinition(Sym, MethodDefinition), + PrivateStaticMethodDefinition(PrivateName, MethodDefinition), + /// A private field definition, only accessible inside the class declaration. - PrivateFieldDefinition(Sym, Option), + PrivateFieldDefinition(PrivateName, Option), + /// A private static field definition, only accessible from static methods and fields inside the /// class declaration. - PrivateStaticFieldDefinition(Sym, Option), + PrivateStaticFieldDefinition(PrivateName, Option), + /// A static block, where a class can have initialization logic for its static fields. StaticBlock(StatementList), } @@ -452,14 +475,14 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - Self::PrivateMethodDefinition(sym, md) - | Self::PrivateStaticMethodDefinition(sym, md) => { - try_break!(visitor.visit_sym(sym)); + Self::PrivateMethodDefinition(name, md) + | Self::PrivateStaticMethodDefinition(name, md) => { + try_break!(visitor.visit_private_name(name)); visitor.visit_method_definition(md) } - Self::PrivateFieldDefinition(sym, maybe_expr) - | Self::PrivateStaticFieldDefinition(sym, maybe_expr) => { - try_break!(visitor.visit_sym(sym)); + Self::PrivateFieldDefinition(name, maybe_expr) + | Self::PrivateStaticFieldDefinition(name, maybe_expr) => { + try_break!(visitor.visit_private_name(name)); if let Some(expr) = maybe_expr { visitor.visit_expression(expr) } else { @@ -487,14 +510,14 @@ impl VisitWith for ClassElement { ControlFlow::Continue(()) } } - Self::PrivateMethodDefinition(sym, md) - | Self::PrivateStaticMethodDefinition(sym, md) => { - try_break!(visitor.visit_sym_mut(sym)); + Self::PrivateMethodDefinition(name, md) + | Self::PrivateStaticMethodDefinition(name, md) => { + try_break!(visitor.visit_private_name_mut(name)); visitor.visit_method_definition_mut(md) } - Self::PrivateFieldDefinition(sym, maybe_expr) - | Self::PrivateStaticFieldDefinition(sym, maybe_expr) => { - try_break!(visitor.visit_sym_mut(sym)); + Self::PrivateFieldDefinition(name, maybe_expr) + | Self::PrivateStaticFieldDefinition(name, maybe_expr) => { + try_break!(visitor.visit_private_name_mut(name)); if let Some(expr) = maybe_expr { visitor.visit_expression_mut(expr) } else { @@ -505,3 +528,52 @@ impl VisitWith for ClassElement { } } } + +/// A private name as defined by the [spec]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-private-names +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PrivateName { + /// The `[[Description]]` internal slot of the private name. + description: Sym, + + /// The indices of the private name are included to ensure that private names are unique. + pub(crate) indices: (usize, usize), +} + +impl PrivateName { + /// Create a new private name. + #[inline] + #[must_use] + pub const fn new(description: Sym) -> Self { + Self { + description, + indices: (0, 0), + } + } + + /// Get the description of the private name. + #[inline] + #[must_use] + pub const fn description(&self) -> Sym { + self.description + } +} + +impl VisitWith for PrivateName { + fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow + where + V: Visitor<'a>, + { + visitor.visit_sym(&self.description) + } + + fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow + where + V: VisitorMut<'a>, + { + visitor.visit_sym_mut(&mut self.description) + } +} diff --git a/boa_ast/src/function/mod.rs b/boa_ast/src/function/mod.rs index c043f07b248..775e8f43d40 100644 --- a/boa_ast/src/function/mod.rs +++ b/boa_ast/src/function/mod.rs @@ -33,7 +33,7 @@ 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}; +pub use class::{Class, ClassElement, PrivateName}; use core::ops::ControlFlow; pub use generator::Generator; pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags}; diff --git a/boa_ast/src/operations.rs b/boa_ast/src/operations.rs index 7919d76e122..f4a7e650759 100644 --- a/boa_ast/src/operations.rs +++ b/boa_ast/src/operations.rs @@ -6,18 +6,19 @@ use core::ops::ControlFlow; use std::convert::Infallible; use boa_interner::Sym; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ declaration::VarDeclaration, expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield}, function::{ ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, - Function, Generator, + Function, Generator, PrivateName, }, property::{MethodDefinition, PropertyDefinition}, statement::LabelledItem, - visitor::{NodeRef, VisitWith, Visitor}, + try_break, + visitor::{NodeRef, VisitWith, Visitor, VisitorMut}, Declaration, Expression, Statement, StatementList, StatementListItem, }; @@ -662,3 +663,107 @@ pub fn top_level_var_declared_names(stmts: &StatementList) -> FxHashSet bool { + /// Visitor used by the function to search for an identifier with the name `arguments`. + #[derive(Debug, Clone)] + struct ClassPrivateNameResolver { + private_environments_stack: Vec>, + top_level_class_index: usize, + } + + impl<'ast> VisitorMut<'ast> for ClassPrivateNameResolver { + type BreakTy = (); + + #[inline] + fn visit_class_mut(&mut self, node: &'ast mut Class) -> ControlFlow { + let mut names = FxHashMap::default(); + + for element in node.elements.iter_mut() { + match element { + ClassElement::PrivateMethodDefinition(name, _) + | ClassElement::PrivateStaticMethodDefinition(name, _) + | ClassElement::PrivateFieldDefinition(name, _) + | ClassElement::PrivateStaticFieldDefinition(name, _) => { + name.indices = ( + self.top_level_class_index, + self.private_environments_stack.len(), + ); + names.insert(name.description(), *name); + } + _ => {} + } + } + + self.private_environments_stack.push(names); + + for element in node.elements.iter_mut() { + match element { + ClassElement::MethodDefinition(name, method) + | ClassElement::StaticMethodDefinition(name, method) => { + try_break!(self.visit_property_name_mut(name)); + try_break!(self.visit_method_definition_mut(method)); + } + ClassElement::PrivateMethodDefinition(_, method) + | ClassElement::PrivateStaticMethodDefinition(_, method) => { + try_break!(self.visit_method_definition_mut(method)); + } + ClassElement::FieldDefinition(name, expression) + | ClassElement::StaticFieldDefinition(name, expression) => { + try_break!(self.visit_property_name_mut(name)); + if let Some(expression) = expression { + try_break!(self.visit_expression_mut(expression)); + } + } + ClassElement::PrivateFieldDefinition(_, expression) + | ClassElement::PrivateStaticFieldDefinition(_, expression) => { + if let Some(expression) = expression { + try_break!(self.visit_expression_mut(expression)); + } + } + ClassElement::StaticBlock(statement_list) => { + try_break!(self.visit_statement_list_mut(statement_list)); + } + } + } + + if let Some(function) = &mut node.constructor { + try_break!(self.visit_function_mut(function)); + } + + self.private_environments_stack.pop(); + + ControlFlow::Continue(()) + } + + #[inline] + fn visit_private_name_mut( + &mut self, + node: &'ast mut PrivateName, + ) -> ControlFlow { + let mut found = false; + + for environment in self.private_environments_stack.iter().rev() { + if let Some(n) = environment.get(&node.description()) { + found = true; + node.indices = n.indices; + break; + } + } + + if found { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + } + } + } + + let mut visitor = ClassPrivateNameResolver { + private_environments_stack: Vec::new(), + top_level_class_index, + }; + + visitor.visit_class_mut(node).is_continue() +} diff --git a/boa_ast/src/position.rs b/boa_ast/src/position.rs index 9286474327f..216693e70f4 100644 --- a/boa_ast/src/position.rs +++ b/boa_ast/src/position.rs @@ -7,7 +7,7 @@ use std::{cmp::Ordering, fmt, num::NonZeroU32}; /// ## Similar Implementations /// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216) #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Position { /// Line number. line_number: NonZeroU32, @@ -55,7 +55,7 @@ impl fmt::Display for Position { /// Note that spans are of the form [start, end) i.e. that the start position is inclusive /// and the end position is exclusive. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Span { start: Position, end: Position, diff --git a/boa_ast/src/property.rs b/boa_ast/src/property.rs index 8317f09cefc..c44b5c550d9 100644 --- a/boa_ast/src/property.rs +++ b/boa_ast/src/property.rs @@ -1,5 +1,6 @@ //! Property definition related types, used in object literals and class definitions. +use crate::function::PrivateName; use crate::try_break; use crate::visitor::{VisitWith, Visitor, VisitorMut}; use boa_interner::{Interner, Sym, ToInternedString}; @@ -354,7 +355,7 @@ pub enum ClassElementName { /// A public property. PropertyName(PropertyName), /// A private property. - PrivateIdentifier(Sym), + PrivateIdentifier(PrivateName), } impl ClassElementName { diff --git a/boa_ast/src/visitor.rs b/boa_ast/src/visitor.rs index 3a1c055a7f5..2b4e59b72d2 100644 --- a/boa_ast/src/visitor.rs +++ b/boa_ast/src/visitor.rs @@ -24,7 +24,7 @@ use crate::{ }, function::{ ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, - FormalParameter, FormalParameterList, Function, Generator, + FormalParameter, FormalParameterList, Function, Generator, PrivateName, }, pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern}, property::{MethodDefinition, PropertyDefinition, PropertyName}, @@ -145,6 +145,7 @@ node_ref! { Identifier, FormalParameterList, ClassElement, + PrivateName, VariableList, Variable, Binding, @@ -230,6 +231,7 @@ pub trait Visitor<'ast>: Sized { define_visit!(visit_identifier, Identifier); define_visit!(visit_formal_parameter_list, FormalParameterList); define_visit!(visit_class_element, ClassElement); + define_visit!(visit_private_name, PrivateName); define_visit!(visit_variable_list, VariableList); define_visit!(visit_variable, Variable); define_visit!(visit_binding, Binding); @@ -312,6 +314,7 @@ pub trait Visitor<'ast>: Sized { NodeRef::Identifier(n) => self.visit_identifier(n), NodeRef::FormalParameterList(n) => self.visit_formal_parameter_list(n), NodeRef::ClassElement(n) => self.visit_class_element(n), + NodeRef::PrivateName(n) => self.visit_private_name(n), NodeRef::VariableList(n) => self.visit_variable_list(n), NodeRef::Variable(n) => self.visit_variable(n), NodeRef::Binding(n) => self.visit_binding(n), @@ -399,6 +402,7 @@ pub trait VisitorMut<'ast>: Sized { define_visit_mut!(visit_identifier_mut, Identifier); define_visit_mut!(visit_formal_parameter_list_mut, FormalParameterList); define_visit_mut!(visit_class_element_mut, ClassElement); + define_visit_mut!(visit_private_name_mut, PrivateName); define_visit_mut!(visit_variable_list_mut, VariableList); define_visit_mut!(visit_variable_mut, Variable); define_visit_mut!(visit_binding_mut, Binding); @@ -481,6 +485,7 @@ pub trait VisitorMut<'ast>: Sized { NodeRefMut::Identifier(n) => self.visit_identifier_mut(n), NodeRefMut::FormalParameterList(n) => self.visit_formal_parameter_list_mut(n), NodeRefMut::ClassElement(n) => self.visit_class_element_mut(n), + NodeRefMut::PrivateName(n) => self.visit_private_name_mut(n), NodeRefMut::VariableList(n) => self.visit_variable_list_mut(n), NodeRefMut::Variable(n) => self.visit_variable_mut(n), NodeRefMut::Binding(n) => self.visit_binding_mut(n), diff --git a/boa_engine/src/builtins/eval/mod.rs b/boa_engine/src/builtins/eval/mod.rs index 8a01e2e25be..0a1398ca3db 100644 --- a/boa_engine/src/builtins/eval/mod.rs +++ b/boa_engine/src/builtins/eval/mod.rs @@ -154,18 +154,20 @@ impl Eval { flags |= Flags::IN_METHOD; } - // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true. - if function_object + let function_object = function_object .as_function() - .expect("must be function object") - .is_derived_constructor() - { + .expect("must be function object"); + + // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true. + if function_object.is_derived_constructor() { flags |= Flags::IN_DERIVED_CONSTRUCTOR; } - // TODO: // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]]. // vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true. + if function_object.class_field_initializer_name().is_some() { + flags |= Flags::IN_CLASS_FIELD_INITIALIZER; + } flags } diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 562fdee5f4d..fe018aed302 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -28,7 +28,7 @@ use crate::{ Context, JsResult, JsString, JsValue, }; use boa_ast::{ - function::FormalParameterList, + function::{FormalParameterList, PrivateName}, operations::{bound_names, contains, lexically_declared_names, ContainsSymbol}, StatementList, }; @@ -126,7 +126,7 @@ pub enum ClassFieldDefinition { Public(PropertyKey, JsFunction), /// A class field definition with a private name. - Private(Sym, JsFunction), + Private(PrivateName, JsFunction), } unsafe impl Trace for ClassFieldDefinition { @@ -176,7 +176,10 @@ pub enum Function { fields: Vec, /// The `[[PrivateMethods]]` internal slot. - private_methods: Vec<(Sym, PrivateElement)>, + private_methods: Vec<(PrivateName, PrivateElement)>, + + /// The class object that this function is associated with. + class_object: Option, }, /// A bytecode async function. @@ -192,6 +195,9 @@ pub enum Function { /// The promise capability record of the async function. promise_capability: PromiseCapability, + + /// The class object that this function is associated with. + class_object: Option, }, /// A bytecode generator function. @@ -204,6 +210,9 @@ pub enum Function { /// The `[[HomeObject]]` internal slot. home_object: Option, + + /// The class object that this function is associated with. + class_object: Option, }, /// A bytecode async generator function. @@ -216,6 +225,9 @@ pub enum Function { /// The `[[HomeObject]]` internal slot. home_object: Option, + + /// The class object that this function is associated with. + class_object: Option, }, } @@ -223,7 +235,7 @@ unsafe impl Trace for Function { custom_trace! {this, { match this { Self::Native { function, .. } => {mark(function)} - Self::Ordinary { code, environments, home_object, fields, private_methods, .. } => { + Self::Ordinary { code, environments, home_object, fields, private_methods, class_object, .. } => { mark(code); mark(environments); mark(home_object); @@ -231,18 +243,21 @@ unsafe impl Trace for Function { for (_, elem) in private_methods { mark(elem); } + mark(class_object); } - Self::Async { code, environments, home_object, promise_capability } => { + Self::Async { code, environments, home_object, promise_capability, class_object } => { mark(code); mark(environments); mark(home_object); mark(promise_capability); + mark(class_object); } - Self::Generator { code, environments, home_object} - | Self::AsyncGenerator { code, environments, home_object} => { + Self::Generator { code, environments, home_object, class_object} + | Self::AsyncGenerator { code, environments, home_object, class_object} => { mark(code); mark(environments); mark(home_object); + mark(class_object); } } }} @@ -276,6 +291,15 @@ impl Function { } } + /// Returns the `[[ClassFieldInitializerName]]` internal slot of the function. + pub(crate) fn class_field_initializer_name(&self) -> Option { + if let Self::Ordinary { code, .. } = self { + code.class_field_initializer_name + } else { + None + } + } + /// Returns a reference to the function `[[HomeObject]]` slot if present. pub(crate) const fn get_home_object(&self) -> Option<&JsObject> { match self { @@ -315,14 +339,14 @@ impl Function { } /// Pushes a private value to the `[[Fields]]` internal slot if present. - pub(crate) fn push_field_private(&mut self, key: Sym, value: JsFunction) { + pub(crate) fn push_field_private(&mut self, key: PrivateName, value: JsFunction) { if let Self::Ordinary { fields, .. } = self { fields.push(ClassFieldDefinition::Private(key, value)); } } /// Returns the values of the `[[PrivateMethods]]` internal slot. - pub(crate) fn get_private_methods(&self) -> &[(Sym, PrivateElement)] { + pub(crate) fn get_private_methods(&self) -> &[(PrivateName, PrivateElement)] { if let Self::Ordinary { private_methods, .. } = self @@ -334,7 +358,7 @@ impl Function { } /// Pushes a private method to the `[[PrivateMethods]]` internal slot if present. - pub(crate) fn push_private_method(&mut self, name: Sym, method: PrivateElement) { + pub(crate) fn push_private_method(&mut self, name: PrivateName, method: PrivateElement) { if let Self::Ordinary { private_methods, .. } = self @@ -354,6 +378,17 @@ impl Function { None } } + + /// Sets the class object. + pub(crate) fn set_class_object(&mut self, object: JsObject) { + match self { + Self::Ordinary { class_object, .. } + | Self::Async { class_object, .. } + | Self::Generator { class_object, .. } + | Self::AsyncGenerator { class_object, .. } => *class_object = Some(object), + Self::Native { .. } => {} + } + } } /// Creates a new member function of a `Object` or `prototype`. @@ -895,7 +930,7 @@ impl BuiltIn for BuiltInFunctionObject { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname -fn set_function_name( +pub(crate) fn set_function_name( function: &JsObject, name: &PropertyKey, prefix: Option, diff --git a/boa_engine/src/bytecompiler/class.rs b/boa_engine/src/bytecompiler/class.rs index e8c615f4b0e..a0e00a243d5 100644 --- a/boa_engine/src/bytecompiler/class.rs +++ b/boa_engine/src/bytecompiler/class.rs @@ -23,21 +23,29 @@ impl ByteCompiler<'_, '_> { /// 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) -> JsResult<()> { - let code = CodeBlock::new( - class.name().map_or(Sym::EMPTY_STRING, Identifier::sym), - 0, - true, - ); + let class_name = class.name().map_or(Sym::EMPTY_STRING, Identifier::sym); + + let code = CodeBlock::new(class_name, 0, true); let mut compiler = ByteCompiler { code_block: code, literals_map: FxHashMap::default(), names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), in_async_generator: false, json_parse: self.json_parse, context: self.context, }; + + if let Some(class_name) = class.name() { + if class.has_binding_identifier() { + compiler.code_block.has_binding_identifier = true; + compiler.context.push_compile_time_environment(false); + compiler.context.create_immutable_binding(class_name, true); + } + } + compiler.context.push_compile_time_environment(true); if let Some(expr) = class.constructor() { @@ -118,6 +126,11 @@ impl ByteCompiler<'_, '_> { compiler.code_block.is_class_constructor = true; } + if class.name().is_some() && class.has_binding_identifier() { + let (_, compile_environment) = compiler.context.pop_compile_time_environment(); + compiler.push_compile_environment(compile_environment); + } + compiler.emit_opcode(Opcode::PushUndefined); compiler.emit_opcode(Opcode::Return); @@ -141,83 +154,83 @@ impl ByteCompiler<'_, '_> { match element { ClassElement::StaticMethodDefinition(name, method_definition) => { self.emit_opcode(Opcode::Dup); - match &method_definition { + match method_definition { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassGetterByName, &[index]); + self.emit(Opcode::DefineClassStaticGetterByName, &[index]); } - PropertyName::Computed(ref name_node) => { + PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassGetterByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticGetterByValue); } }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassSetterByName, &[index]); + self.emit(Opcode::DefineClassStaticSetterByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassSetterByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticSetterByValue); } }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassMethodByName, &[index]); + self.emit(Opcode::DefineClassStaticMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassMethodByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticMethodByValue); } }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassMethodByName, &[index]); + self.emit(Opcode::DefineClassStaticMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassMethodByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticMethodByValue); } }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassMethodByName, &[index]); + self.emit(Opcode::DefineClassStaticMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassMethodByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticMethodByValue); } }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineClassMethodByName, &[index]); + self.emit(Opcode::DefineClassStaticMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; - self.emit_opcode(Opcode::DefineClassMethodByValue); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + self.emit_opcode(Opcode::DefineClassStaticMethodByValue); } }, } @@ -225,35 +238,35 @@ impl ByteCompiler<'_, '_> { // TODO: set names for private methods ClassElement::PrivateStaticMethodDefinition(name, method_definition) => { self.emit_opcode(Opcode::Dup); - match &method_definition { + match method_definition { MethodDefinition::Get(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateMethod, &[index]); } } @@ -275,12 +288,17 @@ impl ByteCompiler<'_, '_> { code_block: field_code, literals_map: FxHashMap::default(), names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), in_async_generator: false, json_parse: self.json_parse, context: self.context, }; + field_compiler.context.push_compile_time_environment(false); + field_compiler + .context + .create_immutable_binding(class_name.into(), true); field_compiler.context.push_compile_time_environment(true); if let Some(node) = field { field_compiler.compile_expr(node, true)?; @@ -290,10 +308,15 @@ impl ByteCompiler<'_, '_> { let (num_bindings, compile_environment) = field_compiler.context.pop_compile_time_environment(); field_compiler.push_compile_environment(compile_environment); + let (_, compile_environment) = + field_compiler.context.pop_compile_time_environment(); + field_compiler.push_compile_environment(compile_environment); field_compiler.code_block.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); - let code = Gc::new(field_compiler.finish()); + let mut code = field_compiler.finish(); + code.class_field_initializer_name = Some(Sym::EMPTY_STRING); + let code = Gc::new(code); let index = self.code_block.functions.len() as u32; self.code_block.functions.push(code); self.emit(Opcode::GetFunction, &[index]); @@ -301,18 +324,23 @@ impl ByteCompiler<'_, '_> { } ClassElement::PrivateFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); - let name_index = self.get_or_insert_name((*name).into()); + let name_index = self.get_or_insert_private_name(*name); let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); let mut field_compiler = ByteCompiler { code_block: field_code, literals_map: FxHashMap::default(), names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), in_async_generator: false, json_parse: self.json_parse, context: self.context, }; + field_compiler.context.push_compile_time_environment(false); + field_compiler + .context + .create_immutable_binding(class_name.into(), true); field_compiler.context.push_compile_time_environment(true); if let Some(node) = field { field_compiler.compile_expr(node, true)?; @@ -322,10 +350,15 @@ impl ByteCompiler<'_, '_> { let (num_bindings, compile_environment) = field_compiler.context.pop_compile_time_environment(); field_compiler.push_compile_environment(compile_environment); + let (_, compile_environment) = + field_compiler.context.pop_compile_time_environment(); + field_compiler.push_compile_environment(compile_environment); field_compiler.code_block.num_bindings = num_bindings; field_compiler.emit_opcode(Opcode::Return); - let code = Gc::new(field_compiler.finish()); + let mut code = field_compiler.finish(); + code.class_field_initializer_name = Some(Sym::EMPTY_STRING); + let code = Gc::new(code); let index = self.code_block.functions.len() as u32; self.code_block.functions.push(code); self.emit(Opcode::GetFunction, &[index]); @@ -333,26 +366,60 @@ impl ByteCompiler<'_, '_> { } ClassElement::StaticFieldDefinition(name, field) => { self.emit_opcode(Opcode::Dup); - match name { + self.emit_opcode(Opcode::Dup); + let name_index = match name { PropertyName::Literal(name) => { - if let Some(node) = field { - self.compile_expr(node, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - } - let index = self.get_or_insert_name((*name).into()); - self.emit(Opcode::DefineOwnPropertyByName, &[index]); + Some(self.get_or_insert_name((*name).into())) } - PropertyName::Computed(name_node) => { - self.compile_expr(name_node, true)?; - self.emit_opcode(Opcode::ToPropertyKey); - if let Some(node) = field { - self.compile_expr(node, true)?; - } else { - self.emit_opcode(Opcode::PushUndefined); - } - self.emit_opcode(Opcode::DefineOwnPropertyByValue); + PropertyName::Computed(name) => { + self.compile_expr(name, true)?; + self.emit_opcode(Opcode::Swap); + None } + }; + let field_code = CodeBlock::new(Sym::EMPTY_STRING, 0, true); + let mut field_compiler = ByteCompiler { + code_block: field_code, + literals_map: FxHashMap::default(), + names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), + bindings_map: FxHashMap::default(), + jump_info: Vec::new(), + in_async_generator: false, + json_parse: self.json_parse, + context: self.context, + }; + field_compiler.context.push_compile_time_environment(false); + field_compiler + .context + .create_immutable_binding(class_name.into(), true); + field_compiler.context.push_compile_time_environment(true); + if let Some(node) = field { + field_compiler.compile_expr(node, true)?; + } else { + field_compiler.emit_opcode(Opcode::PushUndefined); + } + let (num_bindings, compile_environment) = + field_compiler.context.pop_compile_time_environment(); + field_compiler.push_compile_environment(compile_environment); + let (_, compile_environment) = + field_compiler.context.pop_compile_time_environment(); + field_compiler.push_compile_environment(compile_environment); + field_compiler.code_block.num_bindings = num_bindings; + field_compiler.emit_opcode(Opcode::Return); + + let mut code = field_compiler.finish(); + code.class_field_initializer_name = Some(Sym::EMPTY_STRING); + let code = Gc::new(code); + let index = self.code_block.functions.len() as u32; + self.code_block.functions.push(code); + self.emit(Opcode::GetFunction, &[index]); + self.emit_opcode(Opcode::SetHomeObject); + self.emit(Opcode::Call, &[0]); + if let Some(name_index) = name_index { + self.emit(Opcode::DefineOwnPropertyByName, &[name_index]); + } else { + self.emit_opcode(Opcode::DefineOwnPropertyByValue); } } ClassElement::PrivateStaticFieldDefinition(name, field) => { @@ -362,19 +429,25 @@ impl ByteCompiler<'_, '_> { } else { self.emit_opcode(Opcode::PushUndefined); } - let index = self.get_or_insert_name((*name).into()); + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::SetPrivateField, &[index]); } ClassElement::StaticBlock(statement_list) => { self.emit_opcode(Opcode::Dup); let mut compiler = ByteCompiler::new(Sym::EMPTY_STRING, true, false, self.context); + compiler.context.push_compile_time_environment(false); + compiler + .context + .create_immutable_binding(class_name.into(), true); compiler.context.push_compile_time_environment(true); compiler.create_decls(statement_list, false); compiler.compile_statement_list(statement_list, false, false)?; let (num_bindings, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); + let (_, compile_environment) = compiler.context.pop_compile_time_environment(); + compiler.push_compile_environment(compile_environment); compiler.code_block.num_bindings = num_bindings; let code = Gc::new(compiler.finish()); @@ -390,33 +463,33 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::Dup); match method_definition { MethodDefinition::Get(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateGetter, &[index]); } MethodDefinition::Set(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateSetter, &[index]); } MethodDefinition::Ordinary(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Async(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::Generator(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } MethodDefinition::AsyncGenerator(expr) => { - self.function(expr.into(), NodeKind::Expression, true)?; - let index = self.get_or_insert_name((*name).into()); + self.method(expr.into(), NodeKind::Expression, class_name, true)?; + let index = self.get_or_insert_private_name(*name); self.emit(Opcode::PushClassPrivateMethod, &[index]); } } @@ -435,79 +508,79 @@ impl ByteCompiler<'_, '_> { match method_definition { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassGetterByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassGetterByValue); } }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassSetterByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassSetterByValue); } }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::DefineClassMethodByName, &[index]); } PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - self.function(expr.into(), NodeKind::Expression, true)?; + self.method(expr.into(), NodeKind::Expression, class_name, true)?; self.emit_opcode(Opcode::DefineClassMethodByValue); } }, diff --git a/boa_engine/src/bytecompiler/expression/mod.rs b/boa_engine/src/bytecompiler/expression/mod.rs index 664c42b0eea..fdca59a2d2b 100644 --- a/boa_engine/src/bytecompiler/expression/mod.rs +++ b/boa_engine/src/bytecompiler/expression/mod.rs @@ -217,7 +217,7 @@ impl ByteCompiler<'_, '_> { Expression::PropertyAccess(PropertyAccess::Private(access)) => { self.compile_expr(access.target(), true)?; self.emit(Opcode::Dup, &[]); - let index = self.get_or_insert_name(access.field().into()); + let index = self.get_or_insert_private_name(access.field()); self.emit(Opcode::GetPrivateField, &[index]); } expr => { @@ -239,6 +239,7 @@ impl ByteCompiler<'_, '_> { self.emit_opcode(Opcode::PushValueToArray); } self.emit_opcode(Opcode::Dup); + self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::PushNewArray); for raw in template.raws() { @@ -310,7 +311,6 @@ impl ByteCompiler<'_, '_> { // TODO: try to remove this variant somehow Expression::FormalParameterList(_) => unreachable!(), } - Ok(()) } } diff --git a/boa_engine/src/bytecompiler/expression/object_literal.rs b/boa_engine/src/bytecompiler/expression/object_literal.rs index 42ce3da0355..80bf42ec545 100644 --- a/boa_engine/src/bytecompiler/expression/object_literal.rs +++ b/boa_engine/src/bytecompiler/expression/object_literal.rs @@ -37,7 +37,7 @@ impl ByteCompiler<'_, '_> { PropertyName::Computed(name_node) => { self.compile_expr(name_node, true)?; self.emit_opcode(Opcode::ToPropertyKey); - if expr.is_function_definition() { + if expr.is_anonymous_function_definition() { self.emit_opcode(Opcode::Dup); self.compile_expr(expr, true)?; self.emit_opcode(Opcode::SetFunctionName); diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index d9e99669851..fb42e90f0b1 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -20,7 +20,8 @@ pub(crate) struct FunctionCompiler { r#async: bool, strict: bool, arrow: bool, - has_binding_identifier: bool, + binding_identifier: Option, + class_name: Option, } impl FunctionCompiler { @@ -32,7 +33,8 @@ impl FunctionCompiler { r#async: false, strict: false, arrow: false, - has_binding_identifier: false, + binding_identifier: None, + class_name: None, } } @@ -72,8 +74,14 @@ impl FunctionCompiler { } /// Indicate if the function has a binding identifier. - pub(crate) const fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self { - self.has_binding_identifier = has_binding_identifier; + pub(crate) const fn binding_identifier(mut self, binding_identifier: Option) -> Self { + self.binding_identifier = binding_identifier; + self + } + + /// Indicate if the function has a class associated with it. + pub(crate) const fn class_name(mut self, class_name: Sym) -> Self { + self.class_name = Some(class_name); self } @@ -97,6 +105,7 @@ impl FunctionCompiler { code_block: code, literals_map: FxHashMap::default(), names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), in_async_generator: self.generator && self.r#async, @@ -104,12 +113,19 @@ impl FunctionCompiler { context, }; - if self.has_binding_identifier { + if let Some(class_name) = self.class_name { + compiler.context.push_compile_time_environment(false); + compiler + .context + .create_immutable_binding(class_name.into(), true); + } + + if let Some(binding_identifier) = self.binding_identifier { compiler.code_block.has_binding_identifier = true; compiler.context.push_compile_time_environment(false); compiler .context - .create_immutable_binding(self.name.into(), self.strict); + .create_immutable_binding(binding_identifier.into(), self.strict); } compiler.context.push_compile_time_environment(true); @@ -202,7 +218,12 @@ impl FunctionCompiler { compiler.code_block.num_bindings = num_bindings; } - if self.has_binding_identifier { + if self.binding_identifier.is_some() { + let (_, compile_environment) = compiler.context.pop_compile_time_environment(); + compiler.push_compile_environment(compile_environment); + } + + if self.class_name.is_some() { let (_, compile_environment) = compiler.context.pop_compile_time_environment(); compiler.push_compile_environment(compile_environment); } diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index dd2147c1023..a8c662a65dd 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -20,7 +20,7 @@ use boa_ast::{ }, function::{ ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, - FormalParameterList, Function, Generator, + FormalParameterList, Function, Generator, PrivateName, }, operations::bound_names, pattern::Pattern, @@ -226,6 +226,7 @@ pub struct ByteCompiler<'b, 'icu> { code_block: CodeBlock, literals_map: FxHashMap, names_map: FxHashMap, + private_names_map: FxHashMap, bindings_map: FxHashMap, jump_info: Vec, in_async_generator: bool, @@ -249,6 +250,7 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { code_block: CodeBlock::new(name, 0, strict), literals_map: FxHashMap::default(), names_map: FxHashMap::default(), + private_names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), in_async_generator: false, @@ -298,6 +300,19 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { index } + #[inline] + fn get_or_insert_private_name(&mut self, name: PrivateName) -> u32 { + if let Some(index) = self.private_names_map.get(&name) { + return *index; + } + + let index = self.code_block.private_names.len() as u32; + self.code_block.private_names.push(name); + self.private_names_map.insert(name, index); + index + } + + #[inline] fn get_or_insert_binding(&mut self, binding: BindingLocator) -> u32 { if let Some(index) = self.bindings_map.get(&binding) { return *index; @@ -678,7 +693,7 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { } }, PropertyAccess::Private(access) => { - let index = self.get_or_insert_name(access.field().into()); + let index = self.get_or_insert_private_name(access.field()); self.compile_expr(access.target(), true)?; self.emit(Opcode::GetPrivateField, &[index]); } @@ -742,7 +757,8 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { PropertyAccess::Simple(access) => match access.field() { PropertyAccessField::Const(name) => { self.compile_expr(access.target(), true)?; - let result = expr_fn(self, 1); + self.emit_opcode(Opcode::Dup); + let result = expr_fn(self, 2); let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPropertyByName, &[index]); @@ -765,7 +781,7 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { PropertyAccess::Private(access) => { self.compile_expr(access.target(), true)?; let result = expr_fn(self, 1); - let index = self.get_or_insert_name(access.field().into()); + let index = self.get_or_insert_private_name(access.field()); self.emit(Opcode::AssignPrivateField, &[index]); if !use_expr { self.emit(Opcode::Pop, &[]); @@ -774,7 +790,8 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { } PropertyAccess::Super(access) => match access.field() { PropertyAccessField::Const(name) => { - self.emit(Opcode::Super, &[]); + self.emit_opcode(Opcode::Super); + self.emit_opcode(Opcode::This); let result = expr_fn(self, 1); let index = self.get_or_insert_name((*name).into()); self.emit(Opcode::SetPropertyByName, &[index]); @@ -911,7 +928,7 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { PropertyAccess::Private(access) => { self.compile_expr(access.target(), true)?; self.emit_opcode(Opcode::Dup); - let index = self.get_or_insert_name(access.field().into()); + let index = self.get_or_insert_private_name(access.field()); self.emit(Opcode::GetPrivateField, &[index]); } PropertyAccess::Super(access) => { @@ -1022,7 +1039,7 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { } OptionalOperationKind::PrivatePropertyAccess { field } => { self.emit_opcode(Opcode::Dup); - let index = self.get_or_insert_name((*field).into()); + let index = self.get_or_insert_private_name(*field); self.emit(Opcode::GetPrivateField, &[index]); self.emit_opcode(Opcode::RotateLeft); self.emit_u8(3); @@ -1249,13 +1266,98 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> { .. } = function; + let binding_identifier = if has_binding_identifier { + if let Some(name) = name { + Some(name.sym()) + } else { + Some(Sym::EMPTY_STRING) + } + } else { + None + }; + let code = FunctionCompiler::new() .name(name.map(Identifier::sym)) .generator(generator) .r#async(r#async) .strict(self.code_block.strict) .arrow(arrow) - .has_binding_identifier(has_binding_identifier) + .binding_identifier(binding_identifier) + .compile(parameters, body, self.context)?; + + let index = self.code_block.functions.len() as u32; + self.code_block.functions.push(code); + + if r#async && generator { + self.emit(Opcode::GetGeneratorAsync, &[index]); + } else if generator { + self.emit(Opcode::GetGenerator, &[index]); + } else if r#async && arrow { + self.emit(Opcode::GetAsyncArrowFunction, &[index]); + } else if r#async { + self.emit(Opcode::GetFunctionAsync, &[index]); + } else if arrow { + self.emit(Opcode::GetArrowFunction, &[index]); + } else { + self.emit(Opcode::GetFunction, &[index]); + } + + match node_kind { + NodeKind::Declaration => { + self.emit_binding( + BindingOpcode::InitVar, + name.expect("function declaration must have a name"), + ); + } + NodeKind::Expression => { + if !use_expr { + self.emit(Opcode::Pop, &[]); + } + } + } + + Ok(()) + } + + /// Compile a class method AST Node into bytecode. + fn method( + &mut self, + function: FunctionSpec<'_>, + node_kind: NodeKind, + class_name: Sym, + use_expr: bool, + ) -> JsResult<()> { + let (generator, r#async, arrow) = ( + function.is_generator(), + function.is_async(), + function.is_arrow(), + ); + let FunctionSpec { + name, + parameters, + body, + has_binding_identifier, + .. + } = function; + + let binding_identifier = if has_binding_identifier { + if let Some(name) = name { + Some(name.sym()) + } else { + Some(Sym::EMPTY_STRING) + } + } else { + None + }; + + let code = FunctionCompiler::new() + .name(name.map(Identifier::sym)) + .generator(generator) + .r#async(r#async) + .strict(true) + .arrow(arrow) + .binding_identifier(binding_identifier) + .class_name(class_name) .compile(parameters, body, self.context)?; let index = self.code_block.functions.len() as u32; diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index 0f86d4ec6db..a700bc55668 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -11,7 +11,6 @@ use crate::{ Context, JsResult, JsValue, }; use boa_gc::{self, Finalize, Gc, GcCell, Trace}; -use rustc_hash::FxHashMap; use std::{ cell::RefCell, collections::HashMap, @@ -74,7 +73,7 @@ impl JsObject { prototype: prototype.into(), extensible: true, properties: PropertyMap::default(), - private_elements: FxHashMap::default(), + private_elements: Vec::new(), })), } } diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index fe458d4504e..cef668fde8e 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -2,6 +2,7 @@ //! //! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors. +use boa_ast::function::PrivateName; pub use jsobject::{RecursionLimiter, Ref, RefMut}; pub use operations::IntegrityLevel; pub use property_map::*; @@ -55,8 +56,6 @@ use crate::{ }; use boa_gc::{custom_trace, Finalize, GcCell, Trace, WeakGc}; -use boa_interner::Sym; -use rustc_hash::FxHashMap; use std::{ any::Any, fmt::{self, Debug, Display}, @@ -124,7 +123,7 @@ pub struct Object { /// Whether it can have new properties added to it. extensible: bool, /// The `[[PrivateElements]]` internal slot. - private_elements: FxHashMap, + private_elements: Vec<(PrivateName, PrivateElement)>, } unsafe impl Trace for Object { @@ -132,8 +131,8 @@ unsafe impl Trace for Object { mark(&this.data); mark(&this.properties); mark(&this.prototype); - for elem in this.private_elements.values() { - mark(elem); + for (_, element) in &this.private_elements { + mark(element); } }); } @@ -751,7 +750,7 @@ impl Default for Object { properties: PropertyMap::default(), prototype: None, extensible: true, - private_elements: FxHashMap::default(), + private_elements: Vec::default(), } } } @@ -1773,58 +1772,119 @@ impl Object { self.properties.remove(key) } + /// Check if a private name exists. + #[inline] + pub(crate) fn has_private_name(&self, name: &PrivateName, element: &PrivateElement) -> bool { + for (key, value) in &self.private_elements { + if name == key { + if let PrivateElement::Accessor { setter, getter } = element { + if let PrivateElement::Accessor { + setter: s, + getter: g, + } = value + { + if setter.is_some() && s.is_some() || getter.is_some() && g.is_some() { + return true; + } + return false; + } + } + return true; + } + } + + false + } + /// Get a private element. #[inline] - pub(crate) fn get_private_element(&self, name: Sym) -> Option<&PrivateElement> { - self.private_elements.get(&name) + pub(crate) fn get_private_element(&self, name: PrivateName) -> Option<&PrivateElement> { + for (key, value) in &self.private_elements { + if *key == name { + return Some(value); + } + } + None } /// Set a private element. #[inline] - pub(crate) fn set_private_element(&mut self, name: Sym, value: PrivateElement) { - self.private_elements.insert(name, value); + pub(crate) fn set_private_element(&mut self, name: PrivateName, value: PrivateElement) { + for (inner_name, element) in &mut self.private_elements { + if inner_name.description() == name.description() { + *inner_name = name; + *element = value; + return; + } + } + self.private_elements.push((name, value)); } - /// Set a private setter. - pub(crate) fn set_private_element_setter(&mut self, name: Sym, setter: JsObject) { - match self.private_elements.get_mut(&name) { - Some(PrivateElement::Accessor { - getter: _, - setter: s, - }) => { - *s = Some(setter); + /// Assign to an existing private name. + #[inline] + pub(crate) fn assign_private_element( + &mut self, + name: PrivateName, + element: PrivateElement, + ) -> bool { + for (key, value) in &mut self.private_elements { + if *key == name { + *value = element; + return true; } - _ => { - self.private_elements.insert( - name, - PrivateElement::Accessor { - getter: None, - setter: Some(setter), - }, - ); + } + + false + } + + /// Set a private setter. + #[inline] + pub(crate) fn set_private_element_setter(&mut self, name: PrivateName, setter: JsObject) { + for (inner_name, element) in &mut self.private_elements { + if *inner_name == name { + if let PrivateElement::Accessor { + getter: _, + setter: s, + } = element + { + *s = Some(setter); + return; + } } } + + self.private_elements.push(( + name, + PrivateElement::Accessor { + getter: None, + setter: Some(setter), + }, + )); } /// Set a private getter. - pub(crate) fn set_private_element_getter(&mut self, name: Sym, getter: JsObject) { - match self.private_elements.get_mut(&name) { - Some(PrivateElement::Accessor { - getter: g, - setter: _, - }) => { - *g = Some(getter); - } - _ => { - self.private_elements.insert( - name, - PrivateElement::Accessor { - getter: Some(getter), - setter: None, - }, - ); + #[inline] + pub(crate) fn set_private_element_getter(&mut self, name: PrivateName, getter: JsObject) { + for (inner_name, element) in &mut self.private_elements { + if *inner_name == name { + if let PrivateElement::Accessor { + getter: g, + setter: _, + } = element + { + *g = Some(getter); + return; + } } } + + self.private_elements.push(( + name, + PrivateElement::Accessor { + getter: Some(getter), + setter: None, + }, + )); } } diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index c2834fd1ee2..5150e3fdabf 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -23,7 +23,10 @@ use crate::{ vm::{call_frame::FinallyReturn, CallFrame, Opcode}, Context, JsResult, JsString, JsValue, }; -use boa_ast::{expression::Identifier, function::FormalParameterList}; +use boa_ast::{ + expression::Identifier, + function::{FormalParameterList, PrivateName}, +}; use boa_gc::{Finalize, Gc, GcCell, Trace}; use boa_interner::{Interner, Sym, ToInternedString}; use boa_profiler::Profiler; @@ -88,6 +91,10 @@ pub struct CodeBlock { #[unsafe_ignore_trace] pub(crate) names: Vec, + /// Private names. + #[unsafe_ignore_trace] + pub(crate) private_names: Vec, + /// Locators for all bindings in the codeblock. #[unsafe_ignore_trace] pub(crate) bindings: Vec, @@ -108,6 +115,10 @@ pub struct CodeBlock { /// The `[[IsClassConstructor]]` internal slot. pub(crate) is_class_constructor: bool, + /// The `[[ClassFieldInitializerName]]` internal slot. + #[unsafe_ignore_trace] + pub(crate) class_field_initializer_name: Option, + /// Marks the location in the code where the function environment in pushed. /// This is only relevant for functions with expressions in the parameters. /// We execute the parameter expressions in the function code and push the function environment afterward. @@ -123,6 +134,7 @@ impl CodeBlock { code: Vec::new(), literals: Vec::new(), names: Vec::new(), + private_names: Vec::new(), bindings: Vec::new(), num_bindings: 0, functions: Vec::new(), @@ -135,6 +147,7 @@ impl CodeBlock { arguments_binding: None, compile_environments: Vec::new(), is_class_constructor: false, + class_field_initializer_name: None, function_environment_push_location: 0, } } @@ -283,18 +296,28 @@ impl CodeBlock { Opcode::GetPropertyByName | Opcode::SetPropertyByName | Opcode::DefineOwnPropertyByName + | Opcode::DefineClassStaticMethodByName | Opcode::DefineClassMethodByName | Opcode::SetPropertyGetterByName + | Opcode::DefineClassStaticGetterByName | Opcode::DefineClassGetterByName | Opcode::SetPropertySetterByName + | Opcode::DefineClassStaticSetterByName | Opcode::DefineClassSetterByName - | Opcode::AssignPrivateField + | Opcode::DeletePropertyByName => { + let operand = self.read::(*pc); + *pc += size_of::(); + format!( + "{operand:04}: '{}'", + interner.resolve_expect(self.names[operand as usize].sym()), + ) + } + Opcode::AssignPrivateField | Opcode::SetPrivateField | Opcode::SetPrivateMethod | Opcode::SetPrivateSetter | Opcode::SetPrivateGetter | Opcode::GetPrivateField - | Opcode::DeletePropertyByName | Opcode::PushClassFieldPrivate | Opcode::PushClassPrivateGetter | Opcode::PushClassPrivateSetter @@ -303,7 +326,7 @@ impl CodeBlock { *pc += size_of::(); format!( "{operand:04}: '{}'", - interner.resolve_expect(self.names[operand as usize].sym()), + interner.resolve_expect(self.private_names[operand as usize].description()), ) } Opcode::Pop @@ -359,10 +382,13 @@ impl CodeBlock { | Opcode::GetPropertyByValuePush | Opcode::SetPropertyByValue | Opcode::DefineOwnPropertyByValue + | Opcode::DefineClassStaticMethodByValue | Opcode::DefineClassMethodByValue | Opcode::SetPropertyGetterByValue + | Opcode::DefineClassStaticGetterByValue | Opcode::DefineClassGetterByValue | Opcode::SetPropertySetterByValue + | Opcode::DefineClassStaticSetterByValue | Opcode::DefineClassSetterByValue | Opcode::DeletePropertyByValue | Opcode::DeleteSuperThrow @@ -542,6 +568,7 @@ pub(crate) fn create_function_object( environments: context.realm.environments.clone(), home_object: None, promise_capability, + class_object: None, } } else { Function::Ordinary { @@ -551,6 +578,7 @@ pub(crate) fn create_function_object( home_object: None, fields: Vec::new(), private_methods: Vec::new(), + class_object: None, } }; @@ -648,6 +676,7 @@ pub(crate) fn create_generator_function_object( code, environments: context.realm.environments.clone(), home_object: None, + class_object: None, }; JsObject::from_proto_and_data( function_prototype, @@ -658,6 +687,7 @@ pub(crate) fn create_generator_function_object( code, environments: context.realm.environments.clone(), home_object: None, + class_object: None, }; JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function)) }; @@ -716,10 +746,14 @@ impl JsObject { } } Function::Ordinary { - code, environments, .. + code, + environments, + class_object, + .. } => { let code = code.clone(); let mut environments = environments.clone(); + let class_object = class_object.clone(); drop(object); if code.is_class_constructor { @@ -749,6 +783,20 @@ impl JsObject { let compile_time_environment_index = usize::from(code.params.has_expressions()); + if let Some(class_object) = class_object { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + + usize::from(code.has_binding_identifier) + + 1] + .clone(), + ); + context + .realm + .environments + .put_value(index, 0, class_object.into()); + } + if code.has_binding_identifier { let index = context.realm.environments.push_declarative( 1, @@ -841,11 +889,13 @@ impl JsObject { code, environments, promise_capability, + class_object, .. } => { let code = code.clone(); let mut environments = environments.clone(); let promise = promise_capability.promise().clone(); + let class_object = class_object.clone(); drop(object); let environments_len = environments.len(); @@ -869,6 +919,20 @@ impl JsObject { let compile_time_environment_index = usize::from(code.params.has_expressions()); + if let Some(class_object) = class_object { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + + usize::from(code.has_binding_identifier) + + 1] + .clone(), + ); + context + .realm + .environments + .put_value(index, 0, class_object.into()); + } + if code.has_binding_identifier { let index = context.realm.environments.push_declarative( 1, @@ -957,10 +1021,14 @@ impl JsObject { Ok(promise.into()) } Function::Generator { - code, environments, .. + code, + environments, + class_object, + .. } => { let code = code.clone(); let mut environments = environments.clone(); + let class_object = class_object.clone(); drop(object); std::mem::swap(&mut environments, &mut context.realm.environments); @@ -983,6 +1051,20 @@ impl JsObject { let compile_time_environment_index = usize::from(code.params.has_expressions()); + if let Some(class_object) = class_object { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + + usize::from(code.has_binding_identifier) + + 1] + .clone(), + ); + context + .realm + .environments + .put_value(index, 0, class_object.into()); + } + if code.has_binding_identifier { let index = context.realm.environments.push_declarative( 1, @@ -1095,10 +1177,14 @@ impl JsObject { Ok(generator.into()) } Function::AsyncGenerator { - code, environments, .. + code, + environments, + class_object, + .. } => { let code = code.clone(); let mut environments = environments.clone(); + let class_object = class_object.clone(); drop(object); std::mem::swap(&mut environments, &mut context.realm.environments); @@ -1121,6 +1207,20 @@ impl JsObject { let compile_time_environment_index = usize::from(code.params.has_expressions()); + if let Some(class_object) = class_object { + let index = context.realm.environments.push_declarative( + 1, + code.compile_environments[compile_time_environment_index + + usize::from(code.has_binding_identifier) + + 1] + .clone(), + ); + context + .realm + .environments + .put_value(index, 0, class_object.into()); + } + if code.has_binding_identifier { let index = context.realm.environments.push_declarative( 1, @@ -1310,9 +1410,6 @@ impl JsObject { let constructor_kind = *constructor_kind; drop(object); - let environments_len = environments.len(); - std::mem::swap(&mut environments, &mut context.realm.environments); - let this = if constructor_kind.is_base() { // If the prototype of the constructor is not an object, then use the default object // prototype as prototype for the new object @@ -1332,6 +1429,9 @@ impl JsObject { None }; + let environments_len = environments.len(); + std::mem::swap(&mut environments, &mut context.realm.environments); + let new_target = this_target.as_object().expect("must be object"); let compile_time_environment_index = usize::from(code.params.has_expressions()); @@ -1484,6 +1584,12 @@ pub(crate) fn initialize_instance_elements( .expect("class constructor must be function object"); for (name, private_method) in constructor_function.get_private_methods() { + if target.borrow().has_private_name(name, private_method) { + return Err(JsNativeError::typ() + .with_message("Private method already exists on the prototype") + .into()); + } + match private_method { PrivateElement::Method(_) => { target diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index c9364234b88..16eb5f56ca9 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -324,10 +324,13 @@ impl CodeBlock { Opcode::GetPropertyByName | Opcode::SetPropertyByName | Opcode::DefineOwnPropertyByName + | Opcode::DefineClassStaticMethodByName | Opcode::DefineClassMethodByName | Opcode::SetPropertyGetterByName + | Opcode::DefineClassStaticGetterByName | Opcode::DefineClassGetterByName | Opcode::SetPropertySetterByName + | Opcode::DefineClassStaticSetterByName | Opcode::DefineClassSetterByName | Opcode::AssignPrivateField | Opcode::SetPrivateField @@ -414,10 +417,13 @@ impl CodeBlock { | Opcode::GetPropertyByValuePush | Opcode::SetPropertyByValue | Opcode::DefineOwnPropertyByValue + | Opcode::DefineClassStaticMethodByValue | Opcode::DefineClassMethodByValue | Opcode::SetPropertyGetterByValue + | Opcode::DefineClassStaticGetterByValue | Opcode::DefineClassGetterByValue | Opcode::SetPropertySetterByValue + | Opcode::DefineClassStaticSetterByValue | Opcode::DefineClassSetterByValue | Opcode::DeletePropertyByValue | Opcode::DeleteSuperThrow diff --git a/boa_engine/src/vm/opcode/define/class/getter.rs b/boa_engine/src/vm/opcode/define/class/getter.rs index e23b58bb910..a118c2218c7 100644 --- a/boa_engine/src/vm/opcode/define/class/getter.rs +++ b/boa_engine/src/vm/opcode/define/class/getter.rs @@ -1,9 +1,62 @@ use crate::{ + builtins::function::set_function_name, property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, Context, JsResult, JsString, }; +/// `DefineClassStaticGetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticGetterByName` +/// +/// Operation: +/// - Defines a class getter by name. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticGetterByName; + +impl Operation for DefineClassStaticGetterByName { + const NAME: &'static str = "DefineClassStaticGetterByName"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByName"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let index = context.vm.read::(); + let function = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = context + .interner() + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) + .into_common::(false) + .into(); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("get")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + let set = class + .__get_own_property__(&key, context)? + .as_ref() + .and_then(PropertyDescriptor::set) + .cloned(); + class.__define_own_property__( + key, + PropertyDescriptor::builder() + .maybe_get(Some(function)) + .maybe_set(set) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + /// `DefineClassGetterByName` implements the Opcode Operation for `Opcode::DefineClassGetterByName` /// /// Operation: @@ -17,31 +70,91 @@ impl Operation for DefineClassGetterByName { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let name = context.vm.frame().code.names[index as usize]; - let name = context + let function = context.vm.pop(); + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = context .interner() - .resolve_expect(name.sym()) + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) .into_common::(false) .into(); - let set = object - .__get_own_property__(&name, context)? + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("get")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + let set = class_proto + .__get_own_property__(&key, context)? + .as_ref() + .and_then(PropertyDescriptor::set) + .cloned(); + class_proto.__define_own_property__( + key, + PropertyDescriptor::builder() + .maybe_get(Some(function)) + .maybe_set(set) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + +/// `DefineClassStaticGetterByValue` implements the Opcode Operation for `Opcode::DefineClassStaticGetterByValue` +/// +/// Operation: +/// - Defines a class getter by value. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticGetterByValue; + +impl Operation for DefineClassStaticGetterByValue { + const NAME: &'static str = "DefineClassStaticGetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByValue"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let function = context.vm.pop(); + let key = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("get")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + let set = class + .__get_own_property__(&key, context)? .as_ref() .and_then(PropertyDescriptor::set) .cloned(); - object.__define_own_property__( - name, + class.__define_own_property__( + key, PropertyDescriptor::builder() - .maybe_get(Some(value)) + .maybe_get(Some(function)) .maybe_set(set) .enumerable(false) .configurable(true) @@ -64,27 +177,40 @@ impl Operation for DefineClassGetterByValue { const INSTRUCTION: &'static str = "INST - DefineClassGetterByValue"; fn execute(context: &mut Context<'_>) -> JsResult { - let value = context.vm.pop(); + let function = context.vm.pop(); let key = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let name = key.to_property_key(context)?; - let set = object - .__get_own_property__(&name, context)? + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("get")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + let set = class_proto + .__get_own_property__(&key, context)? .as_ref() .and_then(PropertyDescriptor::set) .cloned(); - object.__define_own_property__( - name, + class_proto.__define_own_property__( + key, PropertyDescriptor::builder() - .maybe_get(Some(value)) + .maybe_get(Some(function)) .maybe_set(set) .enumerable(false) .configurable(true) diff --git a/boa_engine/src/vm/opcode/define/class/method.rs b/boa_engine/src/vm/opcode/define/class/method.rs index 442cbb7960e..f2db0553ae6 100644 --- a/boa_engine/src/vm/opcode/define/class/method.rs +++ b/boa_engine/src/vm/opcode/define/class/method.rs @@ -1,9 +1,57 @@ use crate::{ + builtins::function::set_function_name, property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, Context, JsResult, JsString, }; +/// `DefineClassStaticMethodByName` implements the Opcode Operation for `Opcode::DefineClassStaticMethodByName` +/// +/// Operation: +/// - Defines a class method by name. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticMethodByName; + +impl Operation for DefineClassStaticMethodByName { + const NAME: &'static str = "DefineClassStaticMethodByName"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByName"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let index = context.vm.read::(); + let function = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = context + .interner() + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) + .into_common::(false) + .into(); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, None, context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + class.__define_own_property__( + key, + PropertyDescriptor::builder() + .value(function) + .writable(true) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + /// `DefineClassMethodByName` implements the Opcode Operation for `Opcode::DefineClassMethodByName` /// /// Operation: @@ -17,26 +65,81 @@ impl Operation for DefineClassMethodByName { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let name = context.vm.frame().code.names[index as usize]; - let name = context.interner().resolve_expect(name.sym()); - object.__define_own_property__( - name.into_common::(false).into(), + let function = context.vm.pop(); + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = context + .interner() + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) + .into_common::(false) + .into(); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, None, context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + class_proto.__define_own_property__( + key, PropertyDescriptor::builder() - .value(value) + .value(function) + .writable(true) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + +/// `DefineClassStaticMethodByValue` implements the Opcode Operation for `Opcode::DefineClassStaticMethodByValue` +/// +/// Operation: +/// - Defines a class method by value. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticMethodByValue; + +impl Operation for DefineClassStaticMethodByValue { + const NAME: &'static str = "DefineClassStaticMethodByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByValue"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let function = context.vm.pop(); + let key = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, None, context); + let mut function_object_mut = function_object.borrow_mut(); + let function_mut = function_object_mut + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + class.__define_own_property__( + key, + PropertyDescriptor::builder() + .value(function) .writable(true) .enumerable(false) .configurable(true) @@ -55,30 +158,39 @@ impl Operation for DefineClassMethodByName { pub(crate) struct DefineClassMethodByValue; impl Operation for DefineClassMethodByValue { - const NAME: &'static str = "DefineClassMethodByName"; - const INSTRUCTION: &'static str = "INST - DefineClassMethodByName"; + const NAME: &'static str = "DefineClassMethodByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassMethodByValue"; fn execute(context: &mut Context<'_>) -> JsResult { - let value = context.vm.pop(); + let function = context.vm.pop(); let key = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let key = key.to_property_key(context)?; - object.__define_own_property__( + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, None, context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + class_proto.__define_own_property__( key, PropertyDescriptor::builder() - .value(value) + .value(function) .writable(true) .enumerable(false) .configurable(true) diff --git a/boa_engine/src/vm/opcode/define/class/setter.rs b/boa_engine/src/vm/opcode/define/class/setter.rs index 426c865b03d..f1c513e640e 100644 --- a/boa_engine/src/vm/opcode/define/class/setter.rs +++ b/boa_engine/src/vm/opcode/define/class/setter.rs @@ -1,9 +1,62 @@ use crate::{ + builtins::function::set_function_name, property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, Context, JsResult, JsString, }; +/// `DefineClassStaticSetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticSetterByName` +/// +/// Operation: +/// - Defines a class setter by name. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticSetterByName; + +impl Operation for DefineClassStaticSetterByName { + const NAME: &'static str = "DefineClassStaticSetterByName"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByName"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let index = context.vm.read::(); + let function = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = context + .interner() + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) + .into_common::(false) + .into(); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("set")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + let get = class + .__get_own_property__(&key, context)? + .as_ref() + .and_then(PropertyDescriptor::get) + .cloned(); + class.__define_own_property__( + key, + PropertyDescriptor::builder() + .maybe_set(Some(function)) + .maybe_get(get) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + /// `DefineClassSetterByName` implements the Opcode Operation for `Opcode::DefineClassSetterByName` /// /// Operation: @@ -17,31 +70,91 @@ impl Operation for DefineClassSetterByName { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let name = context.vm.frame().code.names[index as usize]; - let name = context + let function = context.vm.pop(); + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = context .interner() - .resolve_expect(name.sym()) + .resolve_expect(context.vm.frame().code.names[index as usize].sym()) .into_common::(false) .into(); - let get = object - .__get_own_property__(&name, context)? + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("set")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + let get = class_proto + .__get_own_property__(&key, context)? + .as_ref() + .and_then(PropertyDescriptor::get) + .cloned(); + class_proto.__define_own_property__( + key, + PropertyDescriptor::builder() + .maybe_set(Some(function)) + .maybe_get(get) + .enumerable(false) + .configurable(true) + .build(), + context, + )?; + Ok(ShouldExit::False) + } +} + +/// `DefineClassStaticSetterByValue` implements the Opcode Operation for `Opcode::DefineClassStaticSetterByValue` +/// +/// Operation: +/// - Defines a class setter by value. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DefineClassStaticSetterByValue; + +impl Operation for DefineClassStaticSetterByValue { + const NAME: &'static str = "DefineClassStaticSetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByValue"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let function = context.vm.pop(); + let key = context.vm.pop(); + let class = context.vm.pop(); + let class = class.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("set")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class.clone()); + function_mut.set_class_object(class.clone()); + } + let get = class + .__get_own_property__(&key, context)? .as_ref() .and_then(PropertyDescriptor::get) .cloned(); - object.__define_own_property__( - name, + class.__define_own_property__( + key, PropertyDescriptor::builder() - .maybe_set(Some(value)) + .maybe_set(Some(function)) .maybe_get(get) .enumerable(false) .configurable(true) @@ -64,27 +177,40 @@ impl Operation for DefineClassSetterByValue { const INSTRUCTION: &'static str = "INST - DefineClassSetterByValue"; fn execute(context: &mut Context<'_>) -> JsResult { - let value = context.vm.pop(); + let function = context.vm.pop(); let key = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; - value - .as_object() - .expect("method must be function object") - .borrow_mut() - .as_function_mut() - .expect("method must be function object") - .set_home_object(object.clone()); - let name = key.to_property_key(context)?; - let get = object - .__get_own_property__(&name, context)? + let class_proto = context.vm.pop(); + let class_proto = class_proto.as_object().expect("class must be object"); + let key = key + .to_property_key(context) + .expect("property key must already be valid"); + { + let function_object = function + .as_object() + .expect("method must be function object"); + set_function_name(function_object, &key, Some(JsString::from("set")), context); + let mut function_object = function_object.borrow_mut(); + let function_mut = function_object + .as_function_mut() + .expect("method must be function object"); + function_mut.set_home_object(class_proto.clone()); + let class = class_proto + .get("constructor", context) + .expect("class prototype must have constructor") + .as_object() + .expect("class must be object") + .clone(); + function_mut.set_class_object(class); + } + let get = class_proto + .__get_own_property__(&key, context)? .as_ref() .and_then(PropertyDescriptor::get) .cloned(); - object.__define_own_property__( - name, + class_proto.__define_own_property__( + key, PropertyDescriptor::builder() - .maybe_set(Some(value)) + .maybe_set(Some(function)) .maybe_get(get) .enumerable(false) .configurable(true) diff --git a/boa_engine/src/vm/opcode/define/own_property.rs b/boa_engine/src/vm/opcode/define/own_property.rs index 24706de340e..a7c72467e21 100644 --- a/boa_engine/src/vm/opcode/define/own_property.rs +++ b/boa_engine/src/vm/opcode/define/own_property.rs @@ -1,7 +1,7 @@ use crate::{ property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, - Context, JsResult, JsString, + Context, JsNativeError, JsResult, JsString, }; /// `DefineOwnPropertyByName` implements the Opcode Operation for `Opcode::DefineOwnPropertyByName` @@ -64,7 +64,7 @@ impl Operation for DefineOwnPropertyByValue { object.to_object(context)? }; let key = key.to_property_key(context)?; - object.__define_own_property__( + let success = object.__define_own_property__( key, PropertyDescriptor::builder() .value(value) @@ -74,6 +74,11 @@ impl Operation for DefineOwnPropertyByValue { .build(), context, )?; + if !success { + return Err(JsNativeError::typ() + .with_message("failed to defined own property") + .into()); + } Ok(ShouldExit::False) } } diff --git a/boa_engine/src/vm/opcode/environment/mod.rs b/boa_engine/src/vm/opcode/environment/mod.rs index a16192bab22..0bcb9cd0898 100644 --- a/boa_engine/src/vm/opcode/environment/mod.rs +++ b/boa_engine/src/vm/opcode/environment/mod.rs @@ -120,8 +120,6 @@ impl Operation for SuperCall { let result = super_constructor.__construct__(&arguments, &new_target, context)?; - initialize_instance_elements(&result, &active_function, context)?; - let this_env = context .realm .environments @@ -134,6 +132,9 @@ impl Operation for SuperCall { .with_message("this already initialized") .into()); } + + initialize_instance_elements(&result, &active_function, context)?; + context.vm.push(result); Ok(ShouldExit::False) } @@ -191,8 +192,6 @@ impl Operation for SuperCallSpread { let result = super_constructor.__construct__(&arguments, &new_target, context)?; - initialize_instance_elements(&result, &active_function, context)?; - let this_env = context .realm .environments @@ -205,6 +204,9 @@ impl Operation for SuperCallSpread { .with_message("this already initialized") .into()); } + + initialize_instance_elements(&result, &active_function, context)?; + context.vm.push(result); Ok(ShouldExit::False) } @@ -257,20 +259,21 @@ impl Operation for SuperCallDerived { let result = super_constructor.__construct__(&arguments, &new_target, context)?; - initialize_instance_elements(&result, &active_function, context)?; - let this_env = context .realm .environments .get_this_environment() .as_function_slots() .expect("super call must be in function environment"); + if !this_env.borrow_mut().bind_this_value(&result) { return Err(JsNativeError::reference() .with_message("this already initialized") .into()); } + initialize_instance_elements(&result, &active_function, context)?; + context.vm.push(result); Ok(ShouldExit::False) } diff --git a/boa_engine/src/vm/opcode/get/private.rs b/boa_engine/src/vm/opcode/get/private.rs index f51cd16269b..fea37dbc2a6 100644 --- a/boa_engine/src/vm/opcode/get/private.rs +++ b/boa_engine/src/vm/opcode/get/private.rs @@ -18,11 +18,11 @@ impl Operation for GetPrivateField { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); if let Some(object) = value.as_object() { let object_borrow_mut = object.borrow(); - if let Some(element) = object_borrow_mut.get_private_element(name.sym()) { + if let Some(element) = object_borrow_mut.get_private_element(name) { match element { PrivateElement::Field(value) => context.vm.push(value.clone()), PrivateElement::Method(method) => context.vm.push(method.clone()), diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index 6a6b0d428d4..8338b4835e6 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -741,7 +741,7 @@ generate_impl! { /// /// Operands: name_index: `u32` /// - /// Stack: object, value **=>** value + /// Stack: object, receiver, value **=>** value SetPropertyByName, /// Sets the name of a function object. @@ -767,11 +767,18 @@ generate_impl! { /// Stack: object, value **=>** DefineOwnPropertyByName, + /// Defines a static class method by name. + /// + /// Operands: name_index: `u32` + /// + /// Stack: class, function **=>** + DefineClassStaticMethodByName, + /// Defines a class method by name. /// /// Operands: name_index: `u32` /// - /// Stack: object, value **=>** + /// Stack: class_proto, function **=>** DefineClassMethodByName, /// Sets a property by value of an object. @@ -790,11 +797,18 @@ generate_impl! { /// Stack: object, key, value **=>** DefineOwnPropertyByValue, + /// Defines a static class method by value. + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticMethodByValue, + /// Defines a class method by value. /// /// Operands: /// - /// Stack: object, key, value **=>** + /// Stack: class_proto, key, function **=>** DefineClassMethodByValue, /// Sets a getter property by name of an object. @@ -806,13 +820,22 @@ generate_impl! { /// Stack: object, value **=>** SetPropertyGetterByName, + /// Defines a static getter class method by name. + /// + /// Like `static get name() value` + /// + /// Operands: name_index: `u32` + /// + /// Stack: class, function **=>** + DefineClassStaticGetterByName, + /// Defines a getter class method by name. /// /// Like `get name() value` /// /// Operands: name_index: `u32` /// - /// Stack: object, value **=>** + /// Stack: class_proto, function **=>** class DefineClassGetterByName, /// Sets a getter property by value of an object. @@ -824,13 +847,22 @@ generate_impl! { /// Stack: object, key, value **=>** SetPropertyGetterByValue, + /// Defines a static getter class method by value. + /// + /// Like `static get [key]() value` + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticGetterByValue, + /// Defines a getter class method by value. /// /// Like `get [key]() value` /// /// Operands: /// - /// Stack: object, key, value **=>** + /// Stack: class_proto, key, function **=>** DefineClassGetterByValue, /// Sets a setter property by name of an object. @@ -842,13 +874,22 @@ generate_impl! { /// Stack: object, value **=>** SetPropertySetterByName, + /// Defines a static setter class method by name. + /// + /// Like `static set name() value` + /// + /// Operands: name_index: `u32` + /// + /// Stack: class, function **=>** + DefineClassStaticSetterByName, + /// Defines a setter class method by name. /// /// Like `set name() value` /// /// Operands: name_index: `u32` /// - /// Stack: object, value **=>** + /// Stack: class_proto, function **=>** DefineClassSetterByName, /// Sets a setter property by value of an object. @@ -860,20 +901,29 @@ generate_impl! { /// Stack: object, key, value **=>** SetPropertySetterByValue, + /// Defines a static setter class method by value. + /// + /// Like `static set [key]() value` + /// + /// Operands: + /// + /// Stack: class, key, function **=>** + DefineClassStaticSetterByValue, + /// Defines a setter class method by value. /// /// Like `set [key]() value` /// /// Operands: /// - /// Stack: object, key, value **=>** + /// Stack: class_proto, key, function **=>** DefineClassSetterByValue, /// Assign the value of a private property of an object by it's name. /// /// Like `obj.#name = value` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object, value **=>** value AssignPrivateField, @@ -882,7 +932,7 @@ generate_impl! { /// /// Like `#name = value` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object, value **=>** SetPrivateField, @@ -891,7 +941,7 @@ generate_impl! { /// /// Like `#name() {}` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object, value **=>** SetPrivateMethod, @@ -900,7 +950,7 @@ generate_impl! { /// /// Like `set #name() {}` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object, value **=>** SetPrivateSetter, @@ -909,7 +959,7 @@ generate_impl! { /// /// Like `get #name() {}` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object, value **=>** SetPrivateGetter, @@ -918,7 +968,7 @@ generate_impl! { /// /// Like `object.#name` /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: object **=>** value GetPrivateField, @@ -932,28 +982,28 @@ generate_impl! { /// Push a private field to the class. /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: class, field_function **=>** PushClassFieldPrivate, /// Push a private getter to the class. /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: class, getter **=>** PushClassPrivateGetter, /// Push a private setter to the class. /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: class, setter **=>** PushClassPrivateSetter, /// Push a private method to the class. /// - /// Operands: name_index: `u32` + /// Operands: private_name_index: `u32` /// /// Stack: class, method **=>** PushClassPrivateMethod, diff --git a/boa_engine/src/vm/opcode/push/class/field.rs b/boa_engine/src/vm/opcode/push/class/field.rs index 309be915e5f..0f076ceafb9 100644 --- a/boa_engine/src/vm/opcode/push/class/field.rs +++ b/boa_engine/src/vm/opcode/push/class/field.rs @@ -32,6 +32,7 @@ impl Operation for PushClassField { .as_object() .expect("class must be function object"); field_function.set_home_object(class_object.clone()); + field_function.set_class_object(class_object.clone()); class_object .borrow_mut() .as_function_mut() @@ -57,7 +58,7 @@ impl Operation for PushClassFieldPrivate { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let field_function_value = context.vm.pop(); let class_value = context.vm.pop(); @@ -72,12 +73,13 @@ impl Operation for PushClassFieldPrivate { .as_object() .expect("class must be function object"); field_function.set_home_object(class_object.clone()); + field_function.set_class_object(class_object.clone()); class_object .borrow_mut() .as_function_mut() .expect("class must be function object") .push_field_private( - name.sym(), + name, JsFunction::from_object_unchecked(field_function_object.clone()), ); Ok(ShouldExit::False) diff --git a/boa_engine/src/vm/opcode/push/class/private.rs b/boa_engine/src/vm/opcode/push/class/private.rs index 56ad1afe405..32132bb8fc8 100644 --- a/boa_engine/src/vm/opcode/push/class/private.rs +++ b/boa_engine/src/vm/opcode/push/class/private.rs @@ -1,5 +1,6 @@ use crate::{ object::PrivateElement, + property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, Context, JsResult, }; @@ -17,17 +18,34 @@ impl Operation for PushClassPrivateMethod { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let method = context.vm.pop(); let method_object = method.as_callable().expect("method must be callable"); + + let name_string = format!("#{}", context.interner().resolve_expect(name.description())); + let desc = PropertyDescriptor::builder() + .value(name_string) + .writable(false) + .enumerable(false) + .configurable(true) + .build(); + method_object + .__define_own_property__("name".into(), desc, context) + .expect("failed to set name property on private method"); + let class = context.vm.pop(); - class - .as_object() - .expect("class must be function object") + let class_object = class.as_object().expect("class must be function object"); + class_object .borrow_mut() .as_function_mut() .expect("class must be function object") - .push_private_method(name.sym(), PrivateElement::Method(method_object.clone())); + .push_private_method(name, PrivateElement::Method(method_object.clone())); + + let mut method_object_mut = method_object.borrow_mut(); + let function = method_object_mut + .as_function_mut() + .expect("method must be function object"); + function.set_class_object(class_object.clone()); Ok(ShouldExit::False) } } @@ -45,23 +63,27 @@ impl Operation for PushClassPrivateGetter { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let getter = context.vm.pop(); let getter_object = getter.as_callable().expect("getter must be callable"); let class = context.vm.pop(); - class - .as_object() - .expect("class must be function object") + let class_object = class.as_object().expect("class must be function object"); + class_object .borrow_mut() .as_function_mut() .expect("class must be function object") .push_private_method( - name.sym(), + name, PrivateElement::Accessor { getter: Some(getter_object.clone()), setter: None, }, ); + let mut getter_object_mut = getter_object.borrow_mut(); + let function = getter_object_mut + .as_function_mut() + .expect("getter must be function object"); + function.set_class_object(class_object.clone()); Ok(ShouldExit::False) } } @@ -79,23 +101,27 @@ impl Operation for PushClassPrivateSetter { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let setter = context.vm.pop(); let setter_object = setter.as_callable().expect("getter must be callable"); let class = context.vm.pop(); - class - .as_object() - .expect("class must be function object") + let class_object = class.as_object().expect("class must be function object"); + class_object .borrow_mut() .as_function_mut() .expect("class must be function object") .push_private_method( - name.sym(), + name, PrivateElement::Accessor { getter: None, setter: Some(setter_object.clone()), }, ); + let mut setter_object_mut = setter_object.borrow_mut(); + let function = setter_object_mut + .as_function_mut() + .expect("setter must be function object"); + function.set_class_object(class_object.clone()); Ok(ShouldExit::False) } } diff --git a/boa_engine/src/vm/opcode/set/home_object.rs b/boa_engine/src/vm/opcode/set/home_object.rs index 06eac0ea57a..c8a1a222b49 100644 --- a/boa_engine/src/vm/opcode/set/home_object.rs +++ b/boa_engine/src/vm/opcode/set/home_object.rs @@ -16,15 +16,18 @@ impl Operation for SetHomeObject { fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); - let function_object = function.as_object().expect("must be object"); let home = context.vm.pop(); - let home_object = home.as_object().expect("must be object"); - function_object - .borrow_mut() - .as_function_mut() - .expect("must be function object") - .set_home_object(home_object.clone()); + { + let function_object = function.as_object().expect("must be object"); + let home_object = home.as_object().expect("must be object"); + let mut function_object_mut = function_object.borrow_mut(); + let function_mut = function_object_mut + .as_function_mut() + .expect("must be function object"); + function_mut.set_home_object(home_object.clone()); + function_mut.set_class_object(home_object.clone()); + } context.vm.push(home); context.vm.push(function); diff --git a/boa_engine/src/vm/opcode/set/private.rs b/boa_engine/src/vm/opcode/set/private.rs index c3c2bf1a7bb..cb052d1da14 100644 --- a/boa_engine/src/vm/opcode/set/private.rs +++ b/boa_engine/src/vm/opcode/set/private.rs @@ -1,6 +1,7 @@ use crate::{ error::JsNativeError, object::PrivateElement, + property::PropertyDescriptor, vm::{opcode::Operation, ShouldExit}, Context, JsResult, }; @@ -18,15 +19,20 @@ impl Operation for AssignPrivateField { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); let object = context.vm.pop(); if let Some(object) = object.as_object() { let mut object_borrow_mut = object.borrow_mut(); - match object_borrow_mut.get_private_element(name.sym()) { + match object_borrow_mut.get_private_element(name) { Some(PrivateElement::Field(_)) => { - object_borrow_mut - .set_private_element(name.sym(), PrivateElement::Field(value.clone())); + if !object_borrow_mut + .assign_private_element(name, PrivateElement::Field(value.clone())) + { + return Err(JsNativeError::typ() + .with_message("cannot assign to private field") + .into()); + } } Some(PrivateElement::Method(_)) => { return Err(JsNativeError::typ() @@ -75,7 +81,7 @@ impl Operation for SetPrivateField { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); let object = context.vm.pop(); if let Some(object) = object.as_object() { @@ -83,13 +89,13 @@ impl Operation for SetPrivateField { if let Some(PrivateElement::Accessor { getter: _, setter: Some(setter), - }) = object_borrow_mut.get_private_element(name.sym()) + }) = object_borrow_mut.get_private_element(name) { let setter = setter.clone(); drop(object_borrow_mut); setter.call(&object.clone().into(), &[value], context)?; } else { - object_borrow_mut.set_private_element(name.sym(), PrivateElement::Field(value)); + object_borrow_mut.set_private_element(name, PrivateElement::Field(value)); } } else { return Err(JsNativeError::typ() @@ -113,14 +119,31 @@ impl Operation for SetPrivateMethod { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); let value = value.as_callable().expect("method must be callable"); + + let name_string = format!("#{}", context.interner().resolve_expect(name.description())); + let desc = PropertyDescriptor::builder() + .value(name_string) + .writable(false) + .enumerable(false) + .configurable(true) + .build(); + value + .__define_own_property__("name".into(), desc, context) + .expect("failed to set name property on private method"); + let object = context.vm.pop(); if let Some(object) = object.as_object() { - let mut object_borrow_mut = object.borrow_mut(); - object_borrow_mut - .set_private_element(name.sym(), PrivateElement::Method(value.clone())); + object + .borrow_mut() + .set_private_element(name, PrivateElement::Method(value.clone())); + let mut value_mut = value.borrow_mut(); + let function = value_mut + .as_function_mut() + .expect("method must be a function"); + function.set_class_object(object.clone()); } else { return Err(JsNativeError::typ() .with_message("cannot set private setter on non-object") @@ -143,13 +166,19 @@ impl Operation for SetPrivateSetter { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); let value = value.as_callable().expect("setter must be callable"); let object = context.vm.pop(); if let Some(object) = object.as_object() { - let mut object_borrow_mut = object.borrow_mut(); - object_borrow_mut.set_private_element_setter(name.sym(), value.clone()); + object + .borrow_mut() + .set_private_element_setter(name, value.clone()); + let mut value_mut = value.borrow_mut(); + let function = value_mut + .as_function_mut() + .expect("method must be a function"); + function.set_class_object(object.clone()); } else { return Err(JsNativeError::typ() .with_message("cannot set private setter on non-object") @@ -172,13 +201,19 @@ impl Operation for SetPrivateGetter { fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); - let name = context.vm.frame().code.names[index as usize]; + let name = context.vm.frame().code.private_names[index as usize]; let value = context.vm.pop(); let value = value.as_callable().expect("getter must be callable"); let object = context.vm.pop(); if let Some(object) = object.as_object() { - let mut object_borrow_mut = object.borrow_mut(); - object_borrow_mut.set_private_element_getter(name.sym(), value.clone()); + object + .borrow_mut() + .set_private_element_getter(name, value.clone()); + let mut value_mut = value.borrow_mut(); + let function = value_mut + .as_function_mut() + .expect("method must be a function"); + function.set_class_object(object.clone()); } else { return Err(JsNativeError::typ() .with_message("cannot set private getter on non-object") diff --git a/boa_engine/src/vm/opcode/set/property.rs b/boa_engine/src/vm/opcode/set/property.rs index 4fa6edbd134..ef2fc65a006 100644 --- a/boa_engine/src/vm/opcode/set/property.rs +++ b/boa_engine/src/vm/opcode/set/property.rs @@ -1,9 +1,9 @@ use crate::{ + builtins::function::set_function_name, property::{PropertyDescriptor, PropertyKey}, vm::{opcode::Operation, ShouldExit}, - Context, JsResult, JsString, + Context, JsNativeError, JsResult, JsString, JsValue, }; -use boa_macros::utf16; /// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName` /// @@ -20,6 +20,7 @@ impl Operation for SetPropertyByName { let index = context.vm.read::(); let value = context.vm.pop(); + let receiver = context.vm.pop(); let object = context.vm.pop(); let object = if let Some(object) = object.as_object() { object.clone() @@ -34,7 +35,13 @@ impl Operation for SetPropertyByName { .into_common::(false) .into(); - object.set(name, value.clone(), context.vm.frame().code.strict, context)?; + //object.set(name, value.clone(), context.vm.frame().code.strict, context)?; + let succeeded = object.__set__(name.clone(), value.clone(), receiver, context)?; + if !succeeded && context.vm.frame().code.strict { + return Err(JsNativeError::typ() + .with_message(format!("cannot set non-writable property: {name}")) + .into()); + } context.vm.stack.push(value); Ok(ShouldExit::False) } @@ -235,39 +242,27 @@ impl Operation for SetFunctionName { fn execute(context: &mut Context<'_>) -> JsResult { let prefix = context.vm.read::(); - let function = context.vm.pop(); let name = context.vm.pop(); - let function_object = function.as_object().expect("function is not an object"); - - let name = if let Some(symbol) = name.as_symbol() { - if let Some(name) = symbol.description() { - JsString::concat_array(&[utf16!("["), &name, utf16!("]")]) - } else { - JsString::from("") - } - } else { - name.as_string().expect("name is not a string").clone() - }; - - let name = match prefix { - 0 => name, - 1 => JsString::concat(utf16!("get "), &name), - 2 => JsString::concat(utf16!("set "), &name), + let name = match name { + JsValue::String(name) => name.into(), + JsValue::Symbol(name) => name.into(), _ => unreachable!(), }; - let desc = PropertyDescriptor::builder() - .value(name) - .writable(false) - .enumerable(false) - .configurable(true) - .build(); + let prefix = match prefix { + 1 => Some(JsString::from("get")), + 2 => Some(JsString::from("set")), + _ => None, + }; - function_object - .__define_own_property__("name".into(), desc, context) - .expect("msg"); + set_function_name( + function.as_object().expect("function is not an object"), + &name, + prefix, + context, + ); context.vm.stack.push(function); Ok(ShouldExit::False) diff --git a/boa_parser/src/parser/cursor/mod.rs b/boa_parser/src/parser/cursor/mod.rs index 9614936624c..95ed9785747 100644 --- a/boa_parser/src/parser/cursor/mod.rs +++ b/boa_parser/src/parser/cursor/mod.rs @@ -3,13 +3,12 @@ mod buffered_lexer; use crate::{ lexer::{InputElement, Lexer, Token, TokenKind}, - parser::{statement::PrivateElement, OrAbrupt, ParseResult}, + parser::{OrAbrupt, ParseResult}, Error, }; use boa_ast::{Position, Punctuator}; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use buffered_lexer::BufferedLexer; -use rustc_hash::FxHashMap; use std::io::Read; /// The result of a peek for a semicolon. @@ -26,8 +25,11 @@ pub(super) enum SemicolonResult<'s> { pub(super) struct Cursor { buffered_lexer: BufferedLexer, - /// Tracks the private identifiers used in code blocks. - private_environments_stack: Vec>, + /// Tracks the number of nested private environments that the cursor is in. + private_environment_nested_index: usize, + + /// Tracks the number of private environments on the root level of the code that is parsed. + private_environment_root_index: usize, /// Tracks if the cursor is in a arrow function declaration. arrow: bool, @@ -44,7 +46,8 @@ where pub(super) fn new(reader: R) -> Self { Self { buffered_lexer: Lexer::new(reader).into(), - private_environments_stack: Vec::new(), + private_environment_nested_index: 0, + private_environment_root_index: 0, arrow: false, json_parse: false, } @@ -128,56 +131,31 @@ where } /// Push a new private environment. + #[inline] pub(super) fn push_private_environment(&mut self) { - let new = FxHashMap::default(); - self.private_environments_stack.push(new); + if !self.in_class() { + self.private_environment_root_index += 1; + } + + self.private_environment_nested_index += 1; } - /// Push a used private identifier. - pub(super) fn push_used_private_identifier( - &mut self, - identifier: Sym, - position: Position, - ) -> ParseResult<()> { - self.private_environments_stack.last_mut().map_or_else( - || { - Err(Error::general( - "private identifier declared outside of class", - position, - )) - }, - |env| { - env.entry(identifier).or_insert(position); - Ok(()) - }, - ) + /// Pop a private environment. + #[inline] + pub(super) fn pop_private_environment(&mut self) { + self.private_environment_nested_index -= 1; } - /// Pop the last private environment. - /// - /// This function takes the private element names of the current class. - /// If a used private identifier is not declared, this throws a syntax error. - pub(super) fn pop_private_environment( - &mut self, - identifiers: &FxHashMap, - ) -> ParseResult<()> { - let last = self - .private_environments_stack - .pop() - .expect("private environment must exist"); - for (identifier, position) in &last { - if !identifiers.contains_key(identifier) { - if let Some(outer) = self.private_environments_stack.last_mut() { - outer.insert(*identifier, *position); - } else { - return Err(Error::general( - "private identifier must be declared", - *position, - )); - } - } - } - Ok(()) + /// Returns the current private environment root index. + #[inline] + pub(super) const fn private_environment_root_index(&self) -> usize { + self.private_environment_root_index + } + + /// Returns if the cursor is in a class. + #[inline] + pub(super) const fn in_class(&self) -> bool { + self.private_environment_nested_index != 0 } /// Returns an error if the next token is not of kind `kind`. diff --git a/boa_parser/src/parser/expression/left_hand_side/call.rs b/boa_parser/src/parser/expression/left_hand_side/call.rs index 3ea3c566039..14ec399af11 100644 --- a/boa_parser/src/parser/expression/left_hand_side/call.rs +++ b/boa_parser/src/parser/expression/left_hand_side/call.rs @@ -16,6 +16,7 @@ use crate::{ }, Error, }; +use ast::function::PrivateName; use boa_ast::{ self as ast, expression::{ @@ -109,8 +110,13 @@ where } TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::PrivateIdentifier(name) => { - cursor.push_used_private_identifier(*name, token.span().start())?; - PrivatePropertyAccess::new(lhs, *name).into() + if !cursor.in_class() { + return Err(Error::general( + "Private identifier outside of class", + token.span().start(), + )); + } + PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() } _ => { return Err(Error::expected( diff --git a/boa_parser/src/parser/expression/left_hand_side/member.rs b/boa_parser/src/parser/expression/left_hand_side/member.rs index 067b62bbce0..4df236e656f 100644 --- a/boa_parser/src/parser/expression/left_hand_side/member.rs +++ b/boa_parser/src/parser/expression/left_hand_side/member.rs @@ -16,6 +16,7 @@ use crate::{ }, Error, }; +use ast::function::PrivateName; use boa_ast::{ self as ast, expression::{ @@ -189,8 +190,13 @@ where } TokenKind::NullLiteral => SimplePropertyAccess::new(lhs, Sym::NULL).into(), TokenKind::PrivateIdentifier(name) => { - cursor.push_used_private_identifier(*name, token.span().start())?; - PrivatePropertyAccess::new(lhs, *name).into() + if !cursor.in_class() { + return Err(Error::general( + "Private identifier outside of class", + token.span().start(), + )); + } + PrivatePropertyAccess::new(lhs, PrivateName::new(*name)).into() } _ => { return Err(Error::expected( diff --git a/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs b/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs index 1653a7c821c..f8dbd51cbb2 100644 --- a/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs +++ b/boa_parser/src/parser/expression/left_hand_side/optional/mod.rs @@ -9,6 +9,7 @@ use crate::{ }, Error, }; +use ast::function::PrivateName; use boa_ast::{ self as ast, expression::{access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind}, @@ -81,8 +82,16 @@ where field: PropertyAccessField::Const(Sym::NULL), }, TokenKind::PrivateIdentifier(name) => { - cursor.push_used_private_identifier(*name, token.span().start())?; - OptionalOperationKind::PrivatePropertyAccess { field: *name } + if !cursor.in_class() { + return Err(Error::general( + "Private identifier outside of class", + token.span().start(), + )); + } + + OptionalOperationKind::PrivatePropertyAccess { + field: PrivateName::new(*name), + } } _ => { return Err(Error::expected( diff --git a/boa_parser/src/parser/expression/primary/class_expression/mod.rs b/boa_parser/src/parser/expression/primary/class_expression/mod.rs index 4d7fcbb6262..0cef2d8fa83 100644 --- a/boa_parser/src/parser/expression/primary/class_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/class_expression/mod.rs @@ -6,7 +6,7 @@ use crate::{ }, }; use boa_ast::{expression::Identifier, function::Class, Keyword}; -use boa_interner::{Interner, Sym}; +use boa_interner::Interner; use boa_profiler::Profiler; use std::io::Read; @@ -50,16 +50,25 @@ where let strict = cursor.strict_mode(); cursor.set_strict_mode(true); + let mut has_binding_identifier = false; let token = cursor.peek(0, interner).or_abrupt()?; let name = match token.kind() { TokenKind::Identifier(_) | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => { + has_binding_identifier = true; BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)? + .into() } - _ => self.name.unwrap_or_else(|| Sym::EMPTY_STRING.into()), + _ => self.name, }; cursor.set_strict_mode(strict); - ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner) + ClassTail::new( + name, + has_binding_identifier, + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner) } } diff --git a/boa_parser/src/parser/expression/primary/object_initializer/mod.rs b/boa_parser/src/parser/expression/primary/object_initializer/mod.rs index 17d9466928d..bb7cff17efa 100644 --- a/boa_parser/src/parser/expression/primary/object_initializer/mod.rs +++ b/boa_parser/src/parser/expression/primary/object_initializer/mod.rs @@ -25,7 +25,9 @@ use boa_ast::{ literal::{self, Literal}, Identifier, }, - function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator}, + function::{ + AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator, PrivateName, + }, operations::{ bound_names, contains, has_direct_super, top_level_lexically_declared_names, ContainsSymbol, }, @@ -611,11 +613,14 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("ClassElementName", "Parsing"); - match cursor.peek(0, interner).or_abrupt()?.kind() { + let token = cursor.peek(0, interner).or_abrupt()?; + match token.kind() { TokenKind::PrivateIdentifier(ident) => { let ident = *ident; cursor.advance(interner); - Ok(property::ClassElementName::PrivateIdentifier(ident)) + Ok(property::ClassElementName::PrivateIdentifier( + PrivateName::new(ident), + )) } _ => Ok(property::ClassElementName::PropertyName( PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?, @@ -719,6 +724,14 @@ where let params = UniqueFormalParameters::new(true, false).parse(cursor, interner)?; + // It is a Syntax Error if UniqueFormalParameters Contains YieldExpression is true. + if contains(¶ms, ContainsSymbol::YieldExpression) { + return Err(Error::lex(LexError::Syntax( + "yield expression not allowed in generator method definition parameters".into(), + params_start_position, + ))); + } + let body_start = cursor .expect( TokenKind::Punctuator(Punctuator::OpenBlock), diff --git a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs index 412703cae9c..a988670f664 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs @@ -14,7 +14,10 @@ use crate::{ }, Error, }; -use ast::operations::{lexically_declared_names, var_declared_names}; +use ast::{ + function::PrivateName, + operations::{class_private_name_resolver, lexically_declared_names, var_declared_names}, +}; use boa_ast::{ self as ast, expression::Identifier, @@ -24,6 +27,7 @@ use boa_ast::{ Declaration, Expression, Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; +use boa_macros::utf16; use rustc_hash::{FxHashMap, FxHashSet}; use std::io::Read; @@ -69,9 +73,11 @@ where let strict = cursor.strict_mode(); cursor.set_strict_mode(true); + let mut has_binding_identifier = false; let token = cursor.peek(0, interner).or_abrupt()?; let name = match token.kind() { TokenKind::Identifier(_) | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => { + has_binding_identifier = true; BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)? } @@ -87,7 +93,13 @@ where cursor.set_strict_mode(strict); Ok(Declaration::Class( - ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?, + ClassTail::new( + name, + has_binding_identifier, + self.allow_yield, + self.allow_await, + ) + .parse(cursor, interner)?, )) } } @@ -100,20 +112,28 @@ where /// [spec]: https://tc39.es/ecma262/#prod-ClassTail #[derive(Debug, Clone, Copy)] pub(in crate::parser) struct ClassTail { - name: Identifier, + name: Option, + has_binding_identifier: bool, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassTail { /// Creates a new `ClassTail` parser. - pub(in crate::parser) fn new(name: Identifier, allow_yield: Y, allow_await: A) -> Self + pub(in crate::parser) fn new( + name: N, + has_binding_identifier: bool, + allow_yield: Y, + allow_await: A, + ) -> Self where + N: Into>, Y: Into, A: Into, { Self { - name, + name: name.into(), + has_binding_identifier, allow_yield: allow_yield.into(), allow_await: allow_await.into(), } @@ -152,17 +172,27 @@ where if is_close_block { cursor.advance(interner); - Ok(Class::new(Some(self.name), super_ref, None, Box::default())) + Ok(Class::new( + self.name, + super_ref, + None, + Box::default(), + self.has_binding_identifier, + )) } else { + cursor.push_private_environment(); + let body_start = cursor.peek(0, interner).or_abrupt()?.span().start(); let (constructor, elements) = ClassBody::new(self.name, self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.expect(Punctuator::CloseBlock, "class tail", interner)?; + cursor.pop_private_environment(); + if super_ref.is_none() { if let Some(constructor) = &constructor { - if contains(constructor, ContainsSymbol::Super) { + if contains(constructor, ContainsSymbol::SuperCall) { return Err(Error::lex(LexError::Syntax( "invalid super usage".into(), body_start, @@ -171,12 +201,24 @@ where } } - Ok(Class::new( - Some(self.name), + let mut class = Class::new( + self.name, super_ref, constructor, elements.into(), - )) + self.has_binding_identifier, + ); + + if !cursor.in_class() + && !class_private_name_resolver(&mut class, cursor.private_environment_root_index()) + { + return Err(Error::lex(LexError::Syntax( + "invalid private name usage".into(), + body_start, + ))); + } + + Ok(class) } } } @@ -238,20 +280,21 @@ where /// [spec]: https://tc39.es/ecma262/#prod-ClassBody #[derive(Debug, Clone, Copy)] pub(in crate::parser) struct ClassBody { - name: Identifier, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassBody { /// Creates a new `ClassBody` parser. - pub(in crate::parser) fn new(name: Identifier, allow_yield: Y, allow_await: A) -> Self + pub(in crate::parser) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where + N: Into>, Y: Into, A: Into, { Self { - name, + name: name.into(), allow_yield: allow_yield.into(), allow_await: allow_await.into(), } @@ -265,8 +308,6 @@ where type Output = (Option, Vec); fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { - cursor.push_private_environment(); - let mut constructor = None; let mut elements = Vec::new(); let mut private_elements_names = FxHashMap::default(); @@ -304,10 +345,12 @@ where } match method { MethodDefinition::Get(_) => { - match private_elements_names.get(name) { + match private_elements_names.get(&name.description()) { Some(PrivateElement::Setter) => { - private_elements_names - .insert(*name, PrivateElement::Value); + private_elements_names.insert( + name.description(), + PrivateElement::Value, + ); } Some(_) => { return Err(Error::general( @@ -316,16 +359,20 @@ where )); } None => { - private_elements_names - .insert(*name, PrivateElement::Getter); + private_elements_names.insert( + name.description(), + PrivateElement::Getter, + ); } } } MethodDefinition::Set(_) => { - match private_elements_names.get(name) { + match private_elements_names.get(&name.description()) { Some(PrivateElement::Getter) => { - private_elements_names - .insert(*name, PrivateElement::Value); + private_elements_names.insert( + name.description(), + PrivateElement::Value, + ); } Some(_) => { return Err(Error::general( @@ -334,14 +381,16 @@ where )); } None => { - private_elements_names - .insert(*name, PrivateElement::Setter); + private_elements_names.insert( + name.description(), + PrivateElement::Setter, + ); } } } _ => { if private_elements_names - .insert(*name, PrivateElement::Value) + .insert(name.description(), PrivateElement::Value) .is_some() { return Err(Error::general( @@ -362,10 +411,12 @@ where } match method { MethodDefinition::Get(_) => { - match private_elements_names.get(name) { + match private_elements_names.get(&name.description()) { Some(PrivateElement::StaticSetter) => { - private_elements_names - .insert(*name, PrivateElement::StaticValue); + private_elements_names.insert( + name.description(), + PrivateElement::StaticValue, + ); } Some(_) => { return Err(Error::general( @@ -374,16 +425,20 @@ where )); } None => { - private_elements_names - .insert(*name, PrivateElement::StaticGetter); + private_elements_names.insert( + name.description(), + PrivateElement::StaticGetter, + ); } } } MethodDefinition::Set(_) => { - match private_elements_names.get(name) { + match private_elements_names.get(&name.description()) { Some(PrivateElement::StaticGetter) => { - private_elements_names - .insert(*name, PrivateElement::StaticValue); + private_elements_names.insert( + name.description(), + PrivateElement::StaticValue, + ); } Some(_) => { return Err(Error::general( @@ -392,14 +447,16 @@ where )); } None => { - private_elements_names - .insert(*name, PrivateElement::StaticSetter); + private_elements_names.insert( + name.description(), + PrivateElement::StaticSetter, + ); } } } _ => { if private_elements_names - .insert(*name, PrivateElement::StaticValue) + .insert(name.description(), PrivateElement::StaticValue) .is_some() { return Err(Error::general( @@ -420,7 +477,7 @@ where } } if private_elements_names - .insert(*name, PrivateElement::Value) + .insert(name.description(), PrivateElement::Value) .is_some() { return Err(Error::general( @@ -439,7 +496,7 @@ where } } if private_elements_names - .insert(*name, PrivateElement::StaticValue) + .insert(name.description(), PrivateElement::StaticValue) .is_some() { return Err(Error::general( @@ -480,7 +537,6 @@ where } cursor.set_strict_mode(strict); - cursor.pop_private_environment(&private_elements_names)?; Ok((constructor, elements)) } @@ -505,20 +561,21 @@ pub(crate) enum PrivateElement { /// [spec]: https://tc39.es/ecma262/#prod-ClassElement #[derive(Debug, Clone, Copy)] pub(in crate::parser) struct ClassElement { - name: Identifier, + name: Option, allow_yield: AllowYield, allow_await: AllowAwait, } impl ClassElement { /// Creates a new `ClassElement` parser. - pub(in crate::parser) fn new(name: Identifier, allow_yield: Y, allow_await: A) -> Self + pub(in crate::parser) fn new(name: N, allow_yield: Y, allow_await: A) -> Self where + N: Into>, Y: Into, A: Into, { Self { - name, + name: name.into(), allow_yield: allow_yield.into(), allow_await: allow_await.into(), } @@ -596,7 +653,7 @@ where )?; cursor.set_strict_mode(strict); - return Ok((Some(Function::new(Some(self.name), parameters, body)), None)); + return Ok((Some(Function::new(self.name, parameters, body)), None)); } TokenKind::Punctuator(Punctuator::OpenBlock) if r#static => { cursor.advance(interner); @@ -645,7 +702,7 @@ where TokenKind::Punctuator(Punctuator::Mul) => { let token = cursor.peek(1, interner).or_abrupt()?; let name_position = token.span().start(); - if let TokenKind::Identifier(Sym::CONSTRUCTOR) = token.kind() { + if token.kind() == &TokenKind::Identifier(Sym::CONSTRUCTOR) && !r#static { return Err(Error::general( "class constructor may not be a generator method", token.span().start(), @@ -671,7 +728,9 @@ where ClassElementName::PropertyName(property_name) => { function::ClassElement::MethodDefinition(property_name, method) } - ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) => { + ClassElementName::PrivateIdentifier(name) + if name.description() == Sym::CONSTRUCTOR => + { return Err(Error::general( "class constructor may not be a private method", name_position, @@ -699,15 +758,13 @@ where TokenKind::Punctuator(Punctuator::Mul) => { let token = cursor.peek(1, interner).or_abrupt()?; let name_position = token.span().start(); - match token.kind() { - TokenKind::Identifier(Sym::CONSTRUCTOR) - | TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => { - return Err(Error::general( - "class constructor may not be a generator method", - token.span().start(), - )); - } - _ => {} + if token.kind() == &TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) + || token.kind() == &TokenKind::Identifier(Sym::CONSTRUCTOR) && !r#static + { + return Err(Error::general( + "class constructor may not be a generator method", + token.span().start(), + )); } let strict = cursor.strict_mode(); cursor.set_strict_mode(true); @@ -745,7 +802,7 @@ where } } } - TokenKind::Identifier(Sym::CONSTRUCTOR) => { + TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { return Err(Error::general( "class constructor may not be an async method", token.span().start(), @@ -776,7 +833,9 @@ where ClassElementName::PropertyName(property_name) => { function::ClassElement::MethodDefinition(property_name, method) } - ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) if r#static => { + ClassElementName::PrivateIdentifier(name) + if name.description() == Sym::CONSTRUCTOR && r#static => + { return Err(Error::general( "class constructor may not be a private method", name_position, @@ -835,12 +894,18 @@ where cursor.set_strict_mode(strict); let method = MethodDefinition::Get(Function::new(None, params, body)); if r#static { - function::ClassElement::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition( + PrivateName::new(name), + method, + ) } else { - function::ClassElement::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition( + PrivateName::new(name), + method, + ) } } - TokenKind::Identifier(Sym::CONSTRUCTOR) => { + TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { return Err(Error::general( "class constructor may not be a getter method", token.span().start(), @@ -956,12 +1021,18 @@ where cursor.set_strict_mode(strict); let method = MethodDefinition::Set(Function::new(None, params, body)); if r#static { - function::ClassElement::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition( + PrivateName::new(name), + method, + ) } else { - function::ClassElement::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition( + PrivateName::new(name), + method, + ) } } - TokenKind::Identifier(Sym::CONSTRUCTOR) => { + TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { return Err(Error::general( "class constructor may not be a setter method", token.span().start(), @@ -1041,6 +1112,11 @@ 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() { @@ -1049,7 +1125,7 @@ where let strict = cursor.strict_mode(); cursor.set_strict_mode(true); let rhs = AssignmentExpression::new( - Some(name.into()), + Some(name_private.into()), true, self.allow_yield, self.allow_await, @@ -1058,9 +1134,15 @@ where cursor.expect_semicolon("expected semicolon", interner)?; cursor.set_strict_mode(strict); if r#static { - function::ClassElement::PrivateStaticFieldDefinition(name, Some(rhs)) + function::ClassElement::PrivateStaticFieldDefinition( + PrivateName::new(name), + Some(rhs), + ) } else { - function::ClassElement::PrivateFieldDefinition(name, Some(rhs)) + function::ClassElement::PrivateFieldDefinition( + PrivateName::new(name), + Some(rhs), + ) } } TokenKind::Punctuator(Punctuator::OpenParen) => { @@ -1092,17 +1174,29 @@ where let method = MethodDefinition::Ordinary(Function::new(None, params, body)); cursor.set_strict_mode(strict); if r#static { - function::ClassElement::PrivateStaticMethodDefinition(name, method) + function::ClassElement::PrivateStaticMethodDefinition( + PrivateName::new(name), + method, + ) } else { - function::ClassElement::PrivateMethodDefinition(name, method) + function::ClassElement::PrivateMethodDefinition( + PrivateName::new(name), + method, + ) } } _ => { cursor.expect_semicolon("expected semicolon", interner)?; if r#static { - function::ClassElement::PrivateStaticFieldDefinition(name, None) + function::ClassElement::PrivateStaticFieldDefinition( + PrivateName::new(name), + None, + ) } else { - function::ClassElement::PrivateFieldDefinition(name, None) + function::ClassElement::PrivateFieldDefinition( + PrivateName::new(name), + None, + ) } } } @@ -1119,7 +1213,7 @@ where let token = cursor.peek(0, interner).or_abrupt()?; match token.kind() { TokenKind::Punctuator(Punctuator::Assign) => { - if let Some(name) = name.prop_name() { + if let Some(name) = name.literal() { if r#static { if [Sym::CONSTRUCTOR, Sym::PROTOTYPE].contains(&name) { return Err(Error::general( @@ -1195,7 +1289,7 @@ where } } _ => { - if let Some(name) = name.prop_name() { + if let Some(name) = name.literal() { if r#static { if [Sym::CONSTRUCTOR, Sym::PROTOTYPE].contains(&name) { return Err(Error::general( diff --git a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs index c0ad145dda1..442dcaf7978 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs @@ -31,6 +31,7 @@ fn check_async_ordinary_method() { None, None, elements.into(), + true, )) .into()], interner, @@ -57,6 +58,7 @@ fn check_async_field_initialization() { None, None, elements.into(), + true, )) .into()], interner, @@ -82,6 +84,7 @@ fn check_async_field() { None, None, elements.into(), + true, )) .into()], interner, diff --git a/boa_parser/src/parser/statement/declaration/mod.rs b/boa_parser/src/parser/statement/declaration/mod.rs index 159671e5099..9fd0a224b03 100644 --- a/boa_parser/src/parser/statement/declaration/mod.rs +++ b/boa_parser/src/parser/statement/declaration/mod.rs @@ -13,7 +13,6 @@ mod lexical; mod tests; pub(in crate::parser) use hoistable::class_decl::ClassTail; -pub(crate) use hoistable::class_decl::PrivateElement; pub(in crate::parser) use hoistable::FunctionDeclaration; use hoistable::HoistableDeclaration; pub(in crate::parser) use lexical::LexicalDeclaration; diff --git a/boa_parser/src/parser/statement/mod.rs b/boa_parser/src/parser/statement/mod.rs index 4a62a346540..76a3ee47182 100644 --- a/boa_parser/src/parser/statement/mod.rs +++ b/boa_parser/src/parser/statement/mod.rs @@ -54,7 +54,6 @@ use boa_profiler::Profiler; use std::io::Read; pub(in crate::parser) use declaration::ClassTail; -pub(crate) use declaration::PrivateElement; /// Statement parsing. ///