diff --git a/boa_engine/src/syntax/ast/node/object/mod.rs b/boa_engine/src/syntax/ast/node/object/mod.rs index a26c77ec51..1f6de9c8da 100644 --- a/boa_engine/src/syntax/ast/node/object/mod.rs +++ b/boa_engine/src/syntax/ast/node/object/mod.rs @@ -338,6 +338,7 @@ pub enum PropertyName { /// /// [spec]: https://tc39.es/ecma262/#prod-LiteralPropertyName Literal(Sym), + /// A `Computed` property name is an expression that gets evaluated and converted into a property name. /// /// More information: @@ -389,3 +390,20 @@ impl From for PropertyName { unsafe impl Trace for PropertyName { unsafe_empty_trace!(); } + +/// `ClassElementName` can be either a property name or a private identifier. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName +#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Finalize)] +pub(crate) enum ClassElementName { + PropertyName(PropertyName), + PrivateIdentifier(Sym), +} + +unsafe impl Trace for ClassElementName { + unsafe_empty_trace!(); +} diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs index 166cc0a43b..3baef73835 100644 --- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -198,12 +198,28 @@ where .kind() == &TokenKind::Punctuator(Punctuator::Mul) { - let (property_name, method) = + let position = cursor + .peek(0, interner)? + .ok_or(ParseError::AbruptEnd)? + .span() + .start(); + let (class_element_name, method) = GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; - return Ok(object::PropertyDefinition::method_definition( - method, - property_name, - )); + + match class_element_name { + object::ClassElementName::PropertyName(property_name) => { + return Ok(object::PropertyDefinition::method_definition( + method, + property_name, + )) + } + object::ClassElementName::PrivateIdentifier(_) => { + return Err(ParseError::general( + "private identifier not allowed in object literal", + position, + )) + } + } } let mut property_name = @@ -455,6 +471,62 @@ where } } +/// `ClassElementName` can be either a property name or a private identifier. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName +#[derive(Debug, Clone)] +pub(in crate::syntax::parser) struct ClassElementName { + allow_yield: AllowYield, + allow_await: AllowAwait, +} + +impl ClassElementName { + /// Creates a new `ClassElementName` parser. + pub(in crate::syntax::parser) fn new(allow_yield: Y, allow_await: A) -> Self + where + Y: Into, + A: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + } + } +} + +impl TokenParser for ClassElementName +where + R: Read, +{ + type Output = object::ClassElementName; + + fn parse( + self, + cursor: &mut Cursor, + interner: &mut Interner, + ) -> Result { + let _timer = Profiler::global().start_event("ClassElementName", "Parsing"); + + match cursor + .peek(0, interner)? + .ok_or(ParseError::AbruptEnd)? + .kind() + { + TokenKind::PrivateIdentifier(ident) => { + let ident = *ident; + cursor.next(interner).expect("token disappeared"); + Ok(object::ClassElementName::PrivateIdentifier(ident)) + } + _ => Ok(object::ClassElementName::PropertyName( + PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?, + )), + } + } +} + /// Initializer parsing. /// /// More information: @@ -537,7 +609,7 @@ impl TokenParser for GeneratorMethod where R: Read, { - type Output = (object::PropertyName, MethodDefinition); + type Output = (object::ClassElementName, MethodDefinition); fn parse( self, @@ -547,8 +619,8 @@ where let _timer = Profiler::global().start_event("GeneratorMethod", "Parsing"); cursor.expect(Punctuator::Mul, "generator method definition", interner)?; - let property_name = - PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; + let class_element_name = + ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; let params = UniqueFormalParameters::new(true, false).parse(cursor, interner)?; @@ -584,7 +656,7 @@ where )?; Ok(( - property_name, + class_element_name, MethodDefinition::Generator(GeneratorExpr::new(None, params, body)), )) } diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs index 368ec7d456..53e46c46ee 100644 --- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs +++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs @@ -617,22 +617,38 @@ where } let strict = cursor.strict_mode(); cursor.set_strict_mode(true); - let (property_name, method) = + let (class_element_name, method) = GeneratorMethod::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; cursor.set_strict_mode(strict); - if r#static { - if let Some(name) = property_name.prop_name() { - if name == Sym::PROTOTYPE { + + match class_element_name { + node::object::ClassElementName::PropertyName(property_name) if r#static => { + if let Some(Sym::PROTOTYPE) = property_name.prop_name() { return Err(ParseError::general( "class may not have static method definitions named 'prototype'", name_position, )); } + ClassElementNode::StaticMethodDefinition(property_name, method) + } + node::object::ClassElementName::PropertyName(property_name) => { + ClassElementNode::MethodDefinition(property_name, method) + } + node::object::ClassElementName::PrivateIdentifier(Sym::CONSTRUCTOR) => { + return Err(ParseError::general( + "class constructor may not be a private method", + name_position, + )) + } + node::object::ClassElementName::PrivateIdentifier(private_ident) + if r#static => + { + ClassElementNode::PrivateStaticMethodDefinition(private_ident, method) + } + node::object::ClassElementName::PrivateIdentifier(private_ident) => { + ClassElementNode::PrivateMethodDefinition(private_ident, method) } - ClassElementNode::StaticMethodDefinition(property_name, method) - } else { - ClassElementNode::MethodDefinition(property_name, method) } } TokenKind::Keyword((Keyword::Async, true)) => {