From 3a6f4a5c680dc9ff97619d7248c1300e4c673d4e Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:14:40 +0100 Subject: [PATCH] Fix various parser idempotency issues and parsing errors (#3917) * Fix various parser idempotency issues and parsing errors * fix lints --- core/ast/src/expression/literal/array.rs | 68 +++++++----- core/ast/src/expression/literal/mod.rs | 22 +++- core/ast/src/expression/literal/object.rs | 103 ++++++++--------- core/ast/src/expression/literal/template.rs | 40 ++++--- core/ast/src/expression/operator/unary/mod.rs | 10 +- core/ast/src/expression/tagged_template.rs | 13 ++- core/ast/src/function/async_function.rs | 6 +- core/ast/src/function/async_generator.rs | 6 +- core/ast/src/function/class.rs | 43 +++---- core/ast/src/function/generator.rs | 6 +- core/ast/src/function/mod.rs | 6 +- core/ast/src/pattern.rs | 11 +- core/ast/src/property.rs | 2 +- .../declaration/declaration_pattern.rs | 31 +++--- .../bytecompiler/expression/object_literal.rs | 35 +++--- core/engine/src/bytecompiler/mod.rs | 25 ++++- core/engine/src/module/source.rs | 4 +- .../primary/array_initializer/tests.rs | 16 +++ .../src/parser/expression/primary/mod.rs | 7 +- .../primary/object_initializer/mod.rs | 47 ++------ .../primary/object_initializer/tests.rs | 42 ++----- .../src/parser/expression/primary/tests.rs | 105 +++++++++++++++++- core/parser/src/parser/function/tests.rs | 18 ++- .../src/parser/statement/block/tests.rs | 6 +- .../hoistable/async_function_decl/mod.rs | 7 +- .../hoistable/async_function_decl/tests.rs | 6 +- .../hoistable/async_generator_decl/mod.rs | 2 +- .../hoistable/async_generator_decl/tests.rs | 2 +- .../hoistable/function_decl/mod.rs | 7 +- .../hoistable/function_decl/tests.rs | 6 +- .../hoistable/generator_decl/mod.rs | 2 +- .../hoistable/generator_decl/tests.rs | 2 +- core/parser/src/parser/statement/mod.rs | 10 +- core/parser/src/parser/statement/throw/mod.rs | 10 +- .../src/parser/statement/throw/tests.rs | 7 +- .../src/parser/tests/format/expression.rs | 45 ++++++++ core/parser/src/parser/tests/mod.rs | 3 +- 37 files changed, 469 insertions(+), 312 deletions(-) diff --git a/core/ast/src/expression/literal/array.rs b/core/ast/src/expression/literal/array.rs index 1cadeb08ae..3ddaec8077 100644 --- a/core/ast/src/expression/literal/array.rs +++ b/core/ast/src/expression/literal/array.rs @@ -1,6 +1,6 @@ //! Array declaration Expression. -use crate::expression::operator::assign::AssignTarget; +use crate::expression::operator::assign::{AssignOp, AssignTarget}; use crate::expression::Expression; use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern}; use crate::try_break; @@ -99,34 +99,39 @@ impl ArrayLiteral { return None; } } - Expression::Assign(assign) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - bindings.push(ArrayPatternElement::SingleName { - ident: *ident, - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::Access(access) => { - bindings.push(ArrayPatternElement::PropertyAccess { - access: access.clone(), - default_init: Some(assign.rhs().clone()), - }); + Expression::Assign(assign) => { + if assign.op() != AssignOp::Assign { + return None; } - AssignTarget::Pattern(pattern) => match pattern { - Pattern::Object(pattern) => { - bindings.push(ArrayPatternElement::Pattern { - pattern: Pattern::Object(pattern.clone()), + match assign.lhs() { + AssignTarget::Identifier(ident) => { + bindings.push(ArrayPatternElement::SingleName { + ident: *ident, default_init: Some(assign.rhs().clone()), }); } - Pattern::Array(pattern) => { - bindings.push(ArrayPatternElement::Pattern { - pattern: Pattern::Array(pattern.clone()), + AssignTarget::Access(access) => { + bindings.push(ArrayPatternElement::PropertyAccess { + access: access.clone(), default_init: Some(assign.rhs().clone()), }); } - }, - }, + AssignTarget::Pattern(pattern) => match pattern { + Pattern::Object(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Object(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + Pattern::Array(pattern) => { + bindings.push(ArrayPatternElement::Pattern { + pattern: Pattern::Array(pattern.clone()), + default_init: Some(assign.rhs().clone()), + }); + } + }, + } + } Expression::ArrayLiteral(array) => { let pattern = array.to_pattern(strict)?.into(); bindings.push(ArrayPatternElement::Pattern { @@ -184,15 +189,18 @@ impl ToInternedString for ArrayLiteral { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = String::from("["); - let mut first = true; - for e in &*self.arr { - if first { - first = false; - } else { - buf.push_str(", "); - } - if let Some(e) = e { + let mut elements = self.arr.iter().peekable(); + + while let Some(element) = elements.next() { + if let Some(e) = element { buf.push_str(&e.to_interned_string(interner)); + if elements.peek().is_some() { + buf.push_str(", "); + } + } else if elements.peek().is_some() { + buf.push_str(", "); + } else { + buf.push(','); } } buf.push(']'); diff --git a/core/ast/src/expression/literal/mod.rs b/core/ast/src/expression/literal/mod.rs index 78414d7e5d..b93995c558 100644 --- a/core/ast/src/expression/literal/mod.rs +++ b/core/ast/src/expression/literal/mod.rs @@ -33,7 +33,6 @@ use super::Expression; /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq)] pub enum Literal { /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. @@ -118,6 +117,25 @@ pub enum Literal { Undefined, } +/// Manual implementation, because `Undefined` is never constructed during parsing. +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for Literal { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let c = >::arbitrary(u)? % 6; + match c { + 0 => Ok(Self::String(::arbitrary(u)?)), + 1 => Ok(Self::Num(::arbitrary(u)?)), + 2 => Ok(Self::Int(::arbitrary(u)?)), + 3 => Ok(Self::BigInt(Box::new( + ::arbitrary(u)?, + ))), + 4 => Ok(Self::Bool(::arbitrary(u)?)), + 5 => Ok(Self::Null), + _ => unreachable!(), + } + } +} + impl From for Literal { #[inline] fn from(string: Sym) -> Self { @@ -176,7 +194,7 @@ impl ToInternedString for Literal { } Self::Num(num) => num.to_string(), Self::Int(num) => num.to_string(), - Self::BigInt(ref num) => num.to_string(), + Self::BigInt(ref num) => format!("{num}n"), Self::Bool(v) => v.to_string(), Self::Null => "null".to_owned(), Self::Undefined => "undefined".to_owned(), diff --git a/core/ast/src/expression/literal/object.rs b/core/ast/src/expression/literal/object.rs index 578deadbf0..bad63edff1 100644 --- a/core/ast/src/expression/literal/object.rs +++ b/core/ast/src/expression/literal/object.rs @@ -2,8 +2,10 @@ use crate::{ block_to_string, - expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT}, - function::Function, + expression::{ + operator::assign::{AssignOp, AssignTarget}, + Expression, RESERVED_IDENTIFIERS_STRICT, + }, join_nodes, pattern::{ObjectPattern, ObjectPatternElement}, property::{MethodDefinition, PropertyDefinition, PropertyName}, @@ -52,7 +54,6 @@ impl ObjectLiteral { #[must_use] pub fn to_pattern(&self, strict: bool) -> Option { let mut bindings = Vec::new(); - let mut excluded_keys = Vec::new(); for (i, property) in self.properties.iter().enumerate() { match property { PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { @@ -63,7 +64,6 @@ impl ObjectLiteral { return None; } - excluded_keys.push(*ident); bindings.push(ObjectPatternElement::SingleName { ident: *ident, name: PropertyName::Literal(ident.sym()), @@ -81,7 +81,6 @@ impl ObjectLiteral { return None; } - excluded_keys.push(*ident); bindings.push(ObjectPatternElement::SingleName { ident: *ident, name: PropertyName::Literal(*name), @@ -111,42 +110,46 @@ impl ObjectLiteral { default_init: None, }); } - (_, Expression::Assign(assign)) => match assign.lhs() { - AssignTarget::Identifier(ident) => { - if let Some(name) = name.literal() { - if name == *ident { - if strict && name == Sym::EVAL { - return None; - } - if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { - return None; + (_, Expression::Assign(assign)) => { + if assign.op() != AssignOp::Assign { + return None; + } + match assign.lhs() { + AssignTarget::Identifier(ident) => { + if let Some(name) = name.literal() { + if name == *ident { + if strict && name == Sym::EVAL { + return None; + } + if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { + return None; + } } - excluded_keys.push(*ident); + bindings.push(ObjectPatternElement::SingleName { + ident: *ident, + name: PropertyName::Literal(name), + default_init: Some(assign.rhs().clone()), + }); + } else { + return None; } - bindings.push(ObjectPatternElement::SingleName { - ident: *ident, - name: PropertyName::Literal(name), + } + AssignTarget::Pattern(pattern) => { + bindings.push(ObjectPatternElement::Pattern { + name: name.clone(), + pattern: pattern.clone(), + default_init: Some(assign.rhs().clone()), + }); + } + AssignTarget::Access(access) => { + bindings.push(ObjectPatternElement::AssignmentPropertyAccess { + name: name.clone(), + access: access.clone(), default_init: Some(assign.rhs().clone()), }); - } else { - return None; } } - AssignTarget::Pattern(pattern) => { - bindings.push(ObjectPatternElement::Pattern { - name: name.clone(), - pattern: pattern.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - AssignTarget::Access(access) => { - bindings.push(ObjectPatternElement::AssignmentPropertyAccess { - name: name.clone(), - access: access.clone(), - default_init: Some(assign.rhs().clone()), - }); - } - }, + } (_, Expression::PropertyAccess(access)) => { bindings.push(ObjectPatternElement::AssignmentPropertyAccess { name: name.clone(), @@ -166,15 +169,11 @@ impl ObjectLiteral { PropertyDefinition::SpreadObject(spread) => { match spread { Expression::Identifier(ident) => { - bindings.push(ObjectPatternElement::RestProperty { - ident: *ident, - excluded_keys: excluded_keys.clone(), - }); + bindings.push(ObjectPatternElement::RestProperty { ident: *ident }); } Expression::PropertyAccess(access) => { bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess { access: access.clone(), - excluded_keys: excluded_keys.clone(), }); } _ => return None, @@ -212,12 +211,6 @@ impl ToIndentedString for ObjectLiteral { format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) } PropertyDefinition::Property(key, value) => { - let value = if let Expression::Function(f) = value { - Function::new(None, f.parameters().clone(), f.body().clone()).into() - } else { - value.clone() - }; - format!( "{indentation}{}: {},\n", key.to_interned_string(interner), @@ -229,13 +222,21 @@ impl ToIndentedString for ObjectLiteral { } PropertyDefinition::MethodDefinition(key, method) => { format!( - "{indentation}{}{}({}) {},\n", + "{indentation}{}({}) {},\n", match &method { - MethodDefinition::Get(_) => "get ", - MethodDefinition::Set(_) => "set ", - _ => "", + MethodDefinition::Get(_) => + format!("get {}", key.to_interned_string(interner)), + MethodDefinition::Set(_) => + format!("set {}", key.to_interned_string(interner)), + MethodDefinition::Ordinary(_) => + key.to_interned_string(interner).to_string(), + MethodDefinition::Generator(_) => + format!("*{}", key.to_interned_string(interner)), + MethodDefinition::AsyncGenerator(_) => + format!("async *{}", key.to_interned_string(interner)), + MethodDefinition::Async(_) => + format!("async {}", key.to_interned_string(interner)), }, - key.to_interned_string(interner), match &method { MethodDefinition::Get(expression) | MethodDefinition::Set(expression) diff --git a/core/ast/src/expression/literal/template.rs b/core/ast/src/expression/literal/template.rs index 1bc6d487b7..ae72124e37 100644 --- a/core/ast/src/expression/literal/template.rs +++ b/core/ast/src/expression/literal/template.rs @@ -1,16 +1,12 @@ //! Template literal Expression. -use core::ops::ControlFlow; -use std::borrow::Cow; - -use boa_interner::{Interner, Sym, ToInternedString}; - use crate::{ expression::Expression, try_break, visitor::{VisitWith, Visitor, VisitorMut}, - ToStringEscaped, }; +use boa_interner::{Interner, Sym, ToInternedString}; +use core::ops::ControlFlow; /// Template literals are string literals allowing embedded expressions. /// @@ -21,12 +17,32 @@ use crate::{ /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals /// [spec]: https://tc39.es/ecma262/#sec-template-literals #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq)] pub struct TemplateLiteral { elements: Box<[TemplateElement]>, } +/// Manual implementation, because string and expression in the element list must always appear in order. +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for TemplateLiteral { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let len = u.arbitrary_len::>()?; + + let mut elements = Vec::with_capacity(len); + for i in 0..len { + if i & 1 == 0 { + elements.push(TemplateElement::String( + ::arbitrary(u)?, + )); + } else { + elements.push(TemplateElement::Expr(Expression::arbitrary(u)?)); + } + } + + Ok(Self::new(elements.into_boxed_slice())) + } +} + impl From for Expression { #[inline] fn from(tem: TemplateLiteral) -> Self { @@ -70,13 +86,11 @@ impl ToInternedString for TemplateLiteral { fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = "`".to_owned(); - for elt in &*self.elements { + for elt in &self.elements { match elt { - TemplateElement::String(s) => buf.push_str(&interner.resolve_expect(*s).join( - Cow::Borrowed, - |utf16| Cow::Owned(utf16.to_string_escaped()), - true, - )), + TemplateElement::String(s) => { + buf.push_str(&format!("{}", interner.resolve_expect(*s))); + } TemplateElement::Expr(n) => { buf.push_str(&format!("${{{}}}", n.to_interned_string(interner))); } diff --git a/core/ast/src/expression/operator/unary/mod.rs b/core/ast/src/expression/operator/unary/mod.rs index f3e8cb244d..3d66014f02 100644 --- a/core/ast/src/expression/operator/unary/mod.rs +++ b/core/ast/src/expression/operator/unary/mod.rs @@ -72,15 +72,7 @@ impl Unary { impl ToInternedString for Unary { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { - let space = match self.op { - UnaryOp::TypeOf | UnaryOp::Delete | UnaryOp::Void => " ", - _ => "", - }; - format!( - "{}{space}{}", - self.op, - self.target.to_interned_string(interner) - ) + format!("{} {}", self.op, self.target.to_interned_string(interner)) } } diff --git a/core/ast/src/expression/tagged_template.rs b/core/ast/src/expression/tagged_template.rs index 75eaf0554f..9d86baa214 100644 --- a/core/ast/src/expression/tagged_template.rs +++ b/core/ast/src/expression/tagged_template.rs @@ -85,12 +85,13 @@ impl ToInternedString for TaggedTemplate { #[inline] fn to_interned_string(&self, interner: &Interner) -> String { let mut buf = format!("{}`", self.tag.to_interned_string(interner)); - for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { - buf.push_str(&format!( - "{}${{{}}}", - interner.resolve_expect(raw), - expr.to_interned_string(interner) - )); + let mut exprs = self.exprs.iter(); + + for raw in &self.raws { + buf.push_str(&format!("{}", interner.resolve_expect(*raw))); + if let Some(expr) = exprs.next() { + buf.push_str(&format!("${{{}}}", expr.to_interned_string(interner))); + } } buf.push('`'); diff --git a/core/ast/src/function/async_function.rs b/core/ast/src/function/async_function.rs index 022d82825b..76789be407 100644 --- a/core/ast/src/function/async_function.rs +++ b/core/ast/src/function/async_function.rs @@ -79,8 +79,10 @@ impl AsyncFunction { impl ToIndentedString for AsyncFunction { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function".to_owned(); - if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + if self.has_binding_identifier { + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } } buf.push_str(&format!( "({}", diff --git a/core/ast/src/function/async_generator.rs b/core/ast/src/function/async_generator.rs index 56d7a88f57..0ced6bbb9f 100644 --- a/core/ast/src/function/async_generator.rs +++ b/core/ast/src/function/async_generator.rs @@ -78,8 +78,10 @@ impl AsyncGenerator { impl ToIndentedString for AsyncGenerator { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "async function*".to_owned(); - if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + if self.has_binding_identifier { + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } } buf.push_str(&format!( "({}) {}", diff --git a/core/ast/src/function/class.rs b/core/ast/src/function/class.rs index c7375ae8d0..3806078e7f 100644 --- a/core/ast/src/function/class.rs +++ b/core/ast/src/function/class.rs @@ -6,11 +6,10 @@ use crate::{ property::{MethodDefinition, PropertyName}, try_break, visitor::{VisitWith, Visitor, VisitorMut}, - Declaration, ToStringEscaped, + Declaration, }; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; -use std::borrow::Cow; use std::hash::Hash; /// A class declaration, as defined by the [spec]. @@ -89,34 +88,24 @@ impl Class { impl ToIndentedString for Class { fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { - let class_name = self.name.map_or(Cow::Borrowed(""), |s| { - interner.resolve_expect(s.sym()).join( - Cow::Borrowed, - |utf16| Cow::Owned(utf16.to_string_escaped()), - true, - ) - }); + let mut buf = "class".to_string(); + if self.has_binding_identifier { + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } + } + if let Some(super_ref) = self.super_ref.as_ref() { + buf.push_str(&format!( + " extends {}", + super_ref.to_interned_string(interner) + )); + } if self.elements.is_empty() && self.constructor().is_none() { - return format!( - "class {class_name}{} {{}}", - self.super_ref - .as_ref() - .map_or_else(String::new, |sup| format!( - " extends {}", - sup.to_interned_string(interner) - )) - ); + buf.push_str(" {}"); + return buf; } let indentation = " ".repeat(indent_n + 1); - let mut buf = format!( - "class {class_name}{} {{\n", - self.super_ref - .as_ref() - .map_or_else(String::new, |sup| format!( - "extends {}", - sup.to_interned_string(interner) - )) - ); + buf.push_str(" {\n"); if let Some(expr) = &self.constructor { buf.push_str(&format!( "{indentation}constructor({}) {}\n", diff --git a/core/ast/src/function/generator.rs b/core/ast/src/function/generator.rs index 50d77611a9..a36515072c 100644 --- a/core/ast/src/function/generator.rs +++ b/core/ast/src/function/generator.rs @@ -80,8 +80,10 @@ impl Generator { impl ToIndentedString for Generator { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "function*".to_owned(); - if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + if self.has_binding_identifier { + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } } buf.push_str(&format!( "({}) {}", diff --git a/core/ast/src/function/mod.rs b/core/ast/src/function/mod.rs index fc6a230868..fc43553162 100644 --- a/core/ast/src/function/mod.rs +++ b/core/ast/src/function/mod.rs @@ -132,8 +132,10 @@ impl Function { impl ToIndentedString for Function { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = "function".to_owned(); - if let Some(name) = self.name { - buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + if self.has_binding_identifier { + if let Some(name) = self.name { + buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); + } } buf.push_str(&format!( "({}) {}", diff --git a/core/ast/src/pattern.rs b/core/ast/src/pattern.rs index c37c5ef9cf..04d4ce5486 100644 --- a/core/ast/src/pattern.rs +++ b/core/ast/src/pattern.rs @@ -308,8 +308,6 @@ pub enum ObjectPatternElement { RestProperty { /// The variable name where the unassigned properties will be stored. ident: Identifier, - /// A list of the excluded property keys that were already destructured. - excluded_keys: Vec, }, /// `AssignmentGetField` represents an `AssignmentProperty` with an expression field member expression `AssignmentElement`. @@ -342,8 +340,6 @@ pub enum ObjectPatternElement { AssignmentRestPropertyAccess { /// The property access where the unassigned properties will be stored. access: PropertyAccess, - /// A list of the excluded property keys that were already destructured. - excluded_keys: Vec, }, /// Pattern represents a property with a `Pattern` as the element. @@ -397,13 +393,10 @@ impl ToInternedString for ObjectPatternElement { } buf } - Self::RestProperty { - ident, - excluded_keys: _, - } => { + Self::RestProperty { ident } => { format!(" ... {}", interner.resolve_expect(ident.sym())) } - Self::AssignmentRestPropertyAccess { access, .. } => { + Self::AssignmentRestPropertyAccess { access } => { format!(" ... {}", access.to_interned_string(interner)) } Self::AssignmentPropertyAccess { diff --git a/core/ast/src/property.rs b/core/ast/src/property.rs index 47ddd3e736..00e6f119a6 100644 --- a/core/ast/src/property.rs +++ b/core/ast/src/property.rs @@ -304,7 +304,7 @@ impl ToInternedString for PropertyName { fn to_interned_string(&self, interner: &Interner) -> String { match self { Self::Literal(key) => interner.resolve_expect(*key).to_string(), - Self::Computed(key) => key.to_interned_string(interner), + Self::Computed(key) => format!("[{}]", key.to_interned_string(interner)), } } } diff --git a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs index 254627d173..2c15ff208f 100644 --- a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs +++ b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs @@ -19,6 +19,7 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::RequireObjectCoercible); + let mut excluded_keys = Vec::new(); let mut additional_excluded_keys_count = 0; let rest_exits = pattern.has_rest(); @@ -40,6 +41,7 @@ impl ByteCompiler<'_> { match name { PropertyName::Literal(name) => { self.emit_get_property_by_name(*name); + excluded_keys.push(*name); } PropertyName::Computed(node) => { self.compile_expr(node, true); @@ -65,15 +67,12 @@ impl ByteCompiler<'_> { } } // BindingRestProperty : ... BindingIdentifier - RestProperty { - ident, - excluded_keys, - } => { + RestProperty { ident } => { self.emit_opcode(Opcode::PushEmptyObject); - for key in excluded_keys { + for key in &excluded_keys { self.emit_push_literal(Literal::String( - self.interner().resolve_expect(key.sym()).into_common(false), + self.interner().resolve_expect(*key).into_common(false), )); } @@ -86,15 +85,12 @@ impl ByteCompiler<'_> { ); self.emit_binding(def, ident.to_js_string(self.interner())); } - AssignmentRestPropertyAccess { - access, - excluded_keys, - } => { + AssignmentRestPropertyAccess { access } => { self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::PushEmptyObject); - for key in excluded_keys { + for key in &excluded_keys { self.emit_push_literal(Literal::String( - self.interner().resolve_expect(key.sym()).into_common(false), + self.interner().resolve_expect(*key).into_common(false), )); } self.emit( @@ -117,10 +113,13 @@ impl ByteCompiler<'_> { } => { self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup); - if let PropertyName::Computed(node) = &name { - self.compile_expr(node, true); - self.emit_opcode(Opcode::ToPropertyKey); - self.emit_opcode(Opcode::Swap); + match &name { + PropertyName::Literal(name) => excluded_keys.push(*name), + PropertyName::Computed(node) => { + self.compile_expr(node, true); + self.emit_opcode(Opcode::ToPropertyKey); + self.emit_opcode(Opcode::Swap); + } } self.access_set( diff --git a/core/engine/src/bytecompiler/expression/object_literal.rs b/core/engine/src/bytecompiler/expression/object_literal.rs index 3fcabb0a3e..29b67ce327 100644 --- a/core/engine/src/bytecompiler/expression/object_literal.rs +++ b/core/engine/src/bytecompiler/expression/object_literal.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, FunctionSpec, Operand}, + bytecompiler::{Access, ByteCompiler, FunctionSpec, MethodKind, Operand}, vm::Opcode, }; use boa_ast::{ @@ -46,7 +46,9 @@ impl ByteCompiler<'_> { PropertyDefinition::MethodDefinition(name, kind) => match kind { MethodDefinition::Get(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Get); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::SetPropertyGetterByName, index); @@ -61,7 +63,9 @@ impl ByteCompiler<'_> { }, MethodDefinition::Set(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Set); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::SetPropertySetterByName, index); @@ -76,7 +80,9 @@ impl ByteCompiler<'_> { }, MethodDefinition::Ordinary(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Ordinary); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); @@ -91,7 +97,9 @@ impl ByteCompiler<'_> { }, MethodDefinition::Async(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Ordinary); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); @@ -106,7 +114,9 @@ impl ByteCompiler<'_> { }, MethodDefinition::Generator(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Ordinary); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); @@ -121,7 +131,9 @@ impl ByteCompiler<'_> { }, MethodDefinition::AsyncGenerator(expr) => match name { PropertyName::Literal(name) => { - self.object_method(expr.into()); + let mut method: FunctionSpec<'_> = expr.into(); + method.name = Some((*name).into()); + self.object_method(method, MethodKind::Ordinary); self.emit_opcode(Opcode::SetHomeObject); let index = self.get_or_insert_name((*name).into()); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); @@ -171,7 +183,7 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::Dup); // stack: object, object, ToPropertyKey(name), ToPropertyKey(name) - self.object_method(function); + self.object_method(function, kind); // stack: object, object, ToPropertyKey(name), ToPropertyKey(name), method let value = match kind { @@ -204,10 +216,3 @@ impl ByteCompiler<'_> { } } } - -#[derive(Debug, Clone, Copy)] -enum MethodKind { - Get, - Set, - Ordinary, -} diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index e5652270c1..4ba14b79a7 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -40,6 +40,7 @@ use boa_ast::{ }; use boa_gc::Gc; use boa_interner::{Interner, Sym}; +use boa_macros::js_str; use rustc_hash::FxHashMap; use thin_vec::ThinVec; @@ -112,7 +113,7 @@ pub(crate) struct FunctionSpec<'a> { pub(crate) name: Option, parameters: &'a FormalParameterList, body: &'a FunctionBody, - has_binding_identifier: bool, + pub(crate) has_binding_identifier: bool, } impl<'a> From<&'a Function> for FunctionSpec<'a> { @@ -187,6 +188,13 @@ impl<'a> From<&'a AsyncGenerator> for FunctionSpec<'a> { } } +#[derive(Debug, Clone, Copy)] +pub(crate) enum MethodKind { + Get, + Set, + Ordinary, +} + /// Represents a callable expression, like `f()` or `new Cl()` #[derive(Debug, Clone, Copy)] enum Callable<'a> { @@ -1346,12 +1354,16 @@ impl<'ctx> ByteCompiler<'ctx> { /// pushing it to the stack if necessary. pub(crate) fn function_with_binding( &mut self, - function: FunctionSpec<'_>, + mut function: FunctionSpec<'_>, node_kind: NodeKind, use_expr: bool, ) { let name = function.name; + if node_kind == NodeKind::Declaration { + function.has_binding_identifier = false; + } + let index = self.function(function); self.emit_with_varying_operand(Opcode::GetFunction, index); @@ -1372,7 +1384,7 @@ impl<'ctx> ByteCompiler<'ctx> { } /// Compile an object method AST Node into bytecode. - pub(crate) fn object_method(&mut self, function: FunctionSpec<'_>) { + pub(crate) fn object_method(&mut self, function: FunctionSpec<'_>, kind: MethodKind) { let (generator, r#async, arrow) = ( function.kind.is_generator(), function.kind.is_async(), @@ -1387,7 +1399,12 @@ impl<'ctx> ByteCompiler<'ctx> { } = function; let name = if let Some(name) = name { - Some(name.sym().to_js_string(self.interner())) + let name = name.sym().to_js_string(self.interner()); + match kind { + MethodKind::Ordinary => Some(name), + MethodKind::Get => Some(js_string!(js_str!("get "), &name)), + MethodKind::Set => Some(js_string!(js_str!("set "), &name)), + } } else { Some(js_string!()) }; diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index 0231686b8d..0e488f9ddb 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1548,7 +1548,7 @@ impl SourceTextModule { // 2. Perform ! env.InitializeBinding(dn, fo). // // deferred to below. - let (spec, locator): (FunctionSpec<'_>, _) = match declaration { + let (mut spec, locator): (FunctionSpec<'_>, _) = match declaration { LexicallyScopedDeclaration::Function(f) => { let name = bound_names(f)[0].to_js_string(compiler.interner()); let locator = env.create_mutable_binding(name, false); @@ -1608,6 +1608,8 @@ impl SourceTextModule { } }; + spec.has_binding_identifier = false; + functions.push((spec, locator)); } diff --git a/core/parser/src/parser/expression/primary/array_initializer/tests.rs b/core/parser/src/parser/expression/primary/array_initializer/tests.rs index 96a3ef9370..d578472f01 100644 --- a/core/parser/src/parser/expression/primary/array_initializer/tests.rs +++ b/core/parser/src/parser/expression/primary/array_initializer/tests.rs @@ -133,3 +133,19 @@ fn check_combined_empty_str() { &mut Interner::default(), ); } + +#[test] +fn check_elision_start_end() { + check_script_parser( + "[, 1 , , ]", + vec![ + Statement::Expression(Expression::from(ArrayLiteral::from(vec![ + None, + Some(Literal::from(1).into()), + None, + ]))) + .into(), + ], + &mut Interner::default(), + ); +} diff --git a/core/parser/src/parser/expression/primary/mod.rs b/core/parser/src/parser/expression/primary/mod.rs index 241ffa9656..bc607fbc40 100644 --- a/core/parser/src/parser/expression/primary/mod.rs +++ b/core/parser/src/parser/expression/primary/mod.rs @@ -47,7 +47,7 @@ use boa_ast::{ self as ast, declaration::Variable, expression::{ - literal::Literal, + literal::{self, Literal, TemplateElement}, operator::{assign::AssignTarget, binary::BinaryOp}, Identifier, Parenthesized, }, @@ -221,9 +221,10 @@ where tok.span().start(), )); }; - let node = Literal::from(cooked).into(); + let temp = + literal::TemplateLiteral::new(Box::new([TemplateElement::String(cooked)])); cursor.advance(interner); - Ok(node) + Ok(temp.into()) } TokenKind::NumericLiteral(Numeric::Integer(num)) => { let node = Literal::from(*num).into(); diff --git a/core/parser/src/parser/expression/primary/object_initializer/mod.rs b/core/parser/src/parser/expression/primary/object_initializer/mod.rs index 4f92aeaaed..46c27c77b7 100644 --- a/core/parser/src/parser/expression/primary/object_initializer/mod.rs +++ b/core/parser/src/parser/expression/primary/object_initializer/mod.rs @@ -39,7 +39,6 @@ use boa_ast::{ Expression, Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; -use boa_macros::utf16; use boa_profiler::Profiler; /// Parses an object literal. @@ -357,15 +356,8 @@ where interner, )?; - let name = property_name.literal().map(|name| { - let s = interner.resolve_expect(name).utf16(); - interner - .get_or_intern([utf16!("get "), s].concat().as_slice()) - .into() - }); - let method = MethodDefinition::Get(Function::new( - name, + None, FormalParameterList::default(), body, )); @@ -450,14 +442,7 @@ where interner, )?; - let name = property_name.literal().map(|name| { - let s = interner.resolve_expect(name).utf16(); - interner - .get_or_intern([utf16!("set "), s].concat().as_slice()) - .into() - }); - - let method = MethodDefinition::Set(Function::new(name, parameters, body)); + let method = MethodDefinition::Set(Function::new(None, parameters, body)); // It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // https://tc39.es/ecma262/#sec-object-initializer-static-semantics-early-errors @@ -526,11 +511,7 @@ where interner, )?; - let method = MethodDefinition::Ordinary(Function::new( - property_name.literal().map(Into::into), - params, - body, - )); + let method = MethodDefinition::Ordinary(Function::new(None, params, body)); // It is a Syntax Error if HasDirectSuper of MethodDefinition is true. if has_direct_super(&method) { @@ -809,12 +790,7 @@ where interner, )?; - let method = MethodDefinition::Generator(Generator::new( - class_element_name.literal().map(Into::into), - params, - body, - false, - )); + let method = MethodDefinition::Generator(Generator::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( @@ -925,12 +901,8 @@ where interner, )?; - let method = MethodDefinition::AsyncGenerator(AsyncGenerator::new( - name.literal().map(Into::into), - params, - body, - false, - )); + let method = + MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( @@ -1018,12 +990,7 @@ where interner, )?; - let method = MethodDefinition::Async(AsyncFunction::new( - class_element_name.literal().map(Into::into), - params, - body, - false, - )); + let method = MethodDefinition::Async(AsyncFunction::new(None, params, body, false)); if contains(&method, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( diff --git a/core/parser/src/parser/expression/primary/object_initializer/tests.rs b/core/parser/src/parser/expression/primary/object_initializer/tests.rs index 98c33ec24f..0fcee57cc8 100644 --- a/core/parser/src/parser/expression/primary/object_initializer/tests.rs +++ b/core/parser/src/parser/expression/primary/object_initializer/tests.rs @@ -63,7 +63,7 @@ fn check_object_short_function() { PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Ordinary(Function::new( - Some(interner.get_or_intern_static("b", utf16!("b")).into()), + None, FormalParameterList::default(), FunctionBody::default(), )), @@ -112,11 +112,7 @@ fn check_object_short_function_arguments() { ), PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), - MethodDefinition::Ordinary(Function::new( - Some(interner.get_or_intern_static("b", utf16!("b")).into()), - parameters, - FunctionBody::default(), - )), + MethodDefinition::Ordinary(Function::new(None, parameters, FunctionBody::default())), ), ]; @@ -151,11 +147,7 @@ fn check_object_getter() { PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), MethodDefinition::Get(Function::new( - Some( - interner - .get_or_intern_static("get b", utf16!("get b")) - .into(), - ), + None, FormalParameterList::default(), FunctionBody::default(), )), @@ -203,15 +195,7 @@ fn check_object_setter() { ), PropertyDefinition::MethodDefinition( interner.get_or_intern_static("b", utf16!("b")).into(), - MethodDefinition::Set(Function::new( - Some( - interner - .get_or_intern_static("set b", utf16!("set b")) - .into(), - ), - params, - FunctionBody::default(), - )), + MethodDefinition::Set(Function::new(None, params, FunctionBody::default())), ), ]; @@ -241,7 +225,7 @@ fn check_object_short_function_get() { let object_properties = vec![PropertyDefinition::MethodDefinition( Sym::GET.into(), MethodDefinition::Ordinary(Function::new( - Some(interner.get_or_intern_static("get", utf16!("get")).into()), + None, FormalParameterList::default(), FunctionBody::default(), )), @@ -272,7 +256,7 @@ fn check_object_short_function_set() { let object_properties = vec![PropertyDefinition::MethodDefinition( Sym::SET.into(), MethodDefinition::Ordinary(Function::new( - Some(interner.get_or_intern_static("set", utf16!("set")).into()), + None, FormalParameterList::default(), FunctionBody::default(), )), @@ -420,7 +404,7 @@ fn check_async_method() { let object_properties = vec![PropertyDefinition::MethodDefinition( interner.get_or_intern_static("dive", utf16!("dive")).into(), MethodDefinition::Async(AsyncFunction::new( - Some(interner.get_or_intern_static("dive", utf16!("dive")).into()), + None, FormalParameterList::default(), FunctionBody::default(), false, @@ -454,11 +438,7 @@ fn check_async_generator_method() { .get_or_intern_static("vroom", utf16!("vroom")) .into(), MethodDefinition::AsyncGenerator(AsyncGenerator::new( - Some( - interner - .get_or_intern_static("vroom", utf16!("vroom")) - .into(), - ), + None, FormalParameterList::default(), FunctionBody::default(), false, @@ -512,11 +492,7 @@ fn check_async_ordinary_method() { let object_properties = vec![PropertyDefinition::MethodDefinition( PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), MethodDefinition::Ordinary(Function::new( - Some( - interner - .get_or_intern_static("async", utf16!("async")) - .into(), - ), + None, FormalParameterList::default(), FunctionBody::default(), )), diff --git a/core/parser/src/parser/expression/primary/tests.rs b/core/parser/src/parser/expression/primary/tests.rs index f78a361bd9..9167c01233 100644 --- a/core/parser/src/parser/expression/primary/tests.rs +++ b/core/parser/src/parser/expression/primary/tests.rs @@ -1,5 +1,17 @@ -use crate::parser::tests::check_script_parser; -use boa_ast::{expression::literal::Literal, Expression, Statement}; +use crate::parser::tests::{check_invalid_script, check_script_parser}; +use boa_ast::{ + expression::{ + literal::{ArrayLiteral, Literal}, + operator::{ + assign::{AssignOp, AssignTarget}, + Assign, + }, + Identifier, Parenthesized, + }, + pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern}, + property::PropertyName, + Expression, Statement, +}; use boa_interner::{Interner, Sym}; use boa_macros::utf16; @@ -23,3 +35,92 @@ fn check_string() { interner, ); } + +#[test] +fn check_destructuring_assignment_object_assignment_operator() { + let interner = &mut Interner::default(); + let a = interner.get_or_intern_static("a", utf16!("a")); + check_script_parser( + "({ a: a = 0 } = 0);", + vec![ + Statement::Expression(Expression::Parenthesized(Parenthesized::new( + Expression::Assign(Assign::new( + AssignOp::Assign, + AssignTarget::Pattern(Pattern::Object(ObjectPattern::from(vec![ + ObjectPatternElement::SingleName { + name: PropertyName::from(a), + ident: Identifier::from(a), + default_init: Some(Literal::from(0).into()), + }, + ]))), + Literal::from(0).into(), + )), + ))) + .into(), + ], + interner, + ); +} + +#[test] +fn check_destructuring_assignment_object_invalid_assignment_operators() { + check_invalid_script("({ a: a &&= 0 } = 0);"); + check_invalid_script("({ a: a ||= 0 } = 0);"); + check_invalid_script("({ a: a ??= 0 } = 0);"); + check_invalid_script("({ a: a *= 0 } = 0);"); + check_invalid_script("({ a: a /= 0 } = 0);"); + check_invalid_script("({ a: a %= 0 } = 0);"); + check_invalid_script("({ a: a += 0 } = 0);"); + check_invalid_script("({ a: a -= 0 } = 0);"); + check_invalid_script("({ a: a <<= 0 } = 0);"); + check_invalid_script("({ a: a >>= 0 } = 0);"); + check_invalid_script("({ a: a >>>= 0 } = 0);"); + check_invalid_script("({ a: a &= 0 } = 0);"); + check_invalid_script("({ a: a ^= 0 } = 0);"); + check_invalid_script("({ a: a |= 0 } = 0);"); + check_invalid_script("({ a: a **= 0 } = 0);"); +} + +#[test] +fn check_destructuring_assignment_array_assignment_operator() { + let interner = &mut Interner::default(); + let a = interner.get_or_intern_static("a", utf16!("a")); + check_script_parser( + "([ a = 0 ] = []);", + vec![ + Statement::Expression(Expression::Parenthesized(Parenthesized::new( + Expression::Assign(Assign::new( + AssignOp::Assign, + AssignTarget::Pattern(Pattern::Array(ArrayPattern::from(vec![ + ArrayPatternElement::SingleName { + ident: Identifier::from(a), + default_init: Some(Literal::from(0).into()), + }, + ]))), + ArrayLiteral::from([]).into(), + )), + ))) + .into(), + ], + interner, + ); +} + +#[test] +fn check_destructuring_assignment_array_invalid_assignment_operators() { + check_invalid_script("([ a &&= 0 ] = []);"); + check_invalid_script("([ a ||= 0 ] = []);"); + check_invalid_script("([ a ??= 0 ] = []);"); + check_invalid_script("([ a *= 0 ] = []);"); + check_invalid_script("([ a /= 0 ] = []);"); + check_invalid_script("([ a %= 0 ] = []);"); + check_invalid_script("([ a += 0 ] = []);"); + check_invalid_script("([ a -= 0 ] = []);"); + check_invalid_script("([ a <<= 0 ] = []);"); + check_invalid_script("([ a >>= 0 ] = []);"); + check_invalid_script("([ a >>>= 0 ] = []);"); + check_invalid_script("([ a &= 0 ] = []);"); + check_invalid_script("([ a ^= 0 ] = []);"); + check_invalid_script("([ a |= 0 ] = []);"); + check_invalid_script("([ a **= 0 ] = []);"); +} diff --git a/core/parser/src/parser/function/tests.rs b/core/parser/src/parser/function/tests.rs index b74f564369..7d8857e285 100644 --- a/core/parser/src/parser/function/tests.rs +++ b/core/parser/src/parser/function/tests.rs @@ -28,7 +28,7 @@ fn check_basic() { check_script_parser( "function foo(a) { return a; }", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::new( @@ -39,6 +39,7 @@ fn check_basic() { ))] .into(), ), + true, )) .into()], interner, @@ -66,7 +67,7 @@ fn check_duplicates_strict_off() { assert_eq!(params.length(), 2); check_script_parser( "function foo(a, a) { return a; }", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::new( @@ -77,6 +78,7 @@ fn check_duplicates_strict_off() { ))] .into(), ), + true, )) .into()], interner, @@ -102,7 +104,7 @@ fn check_basic_semicolon_insertion() { check_script_parser( "function foo(a) { return a }", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::new( @@ -113,6 +115,7 @@ fn check_basic_semicolon_insertion() { ))] .into(), ), + true, )) .into()], interner, @@ -131,7 +134,7 @@ fn check_empty_return() { assert_eq!(params.length(), 1); check_script_parser( "function foo(a) { return; }", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::new( @@ -140,6 +143,7 @@ fn check_empty_return() { ))] .into(), ), + true, )) .into()], interner, @@ -158,7 +162,7 @@ fn check_empty_return_semicolon_insertion() { assert_eq!(params.length(), 1); check_script_parser( "function foo(a) { return }", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::new( @@ -167,6 +171,7 @@ fn check_empty_return_semicolon_insertion() { ))] .into(), ), + true, )) .into()], interner, @@ -194,10 +199,11 @@ fn check_rest_operator() { assert_eq!(params.length(), 1); check_script_parser( "function foo(a, ...b) {}", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some(interner.get_or_intern_static("foo", utf16!("foo")).into()), params, FunctionBody::default(), + true, )) .into()], interner, diff --git a/core/parser/src/parser/statement/block/tests.rs b/core/parser/src/parser/statement/block/tests.rs index 4015c29eeb..b3897e447d 100644 --- a/core/parser/src/parser/statement/block/tests.rs +++ b/core/parser/src/parser/statement/block/tests.rs @@ -78,7 +78,7 @@ fn non_empty() { a++; }", vec![ - Declaration::Function(Function::new( + Declaration::Function(Function::new_with_binding_identifier( Some(hello.into()), FormalParameterList::default(), FunctionBody::new( @@ -87,6 +87,7 @@ fn non_empty() { ))] .into(), ), + true, )) .into(), Statement::Var(VarDeclaration( @@ -135,7 +136,7 @@ fn hoisting() { UpdateTarget::Identifier(Identifier::new(a)), ))) .into(), - Declaration::Function(Function::new( + Declaration::Function(Function::new_with_binding_identifier( Some(hello.into()), FormalParameterList::default(), FunctionBody::new( @@ -144,6 +145,7 @@ fn hoisting() { ))] .into(), ), + true, )) .into(), ], diff --git a/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs index f48f3c4fd1..e0be6e13d8 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -93,11 +93,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(AsyncFunction::new( - Some(result.0), - result.1, - result.2, - false, - )) + Ok(AsyncFunction::new(Some(result.0), result.1, result.2, true)) } } diff --git a/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs b/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs index 6984959468..dc24d56106 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs @@ -20,7 +20,7 @@ fn async_function_declaration() { ), FormalParameterList::default(), FunctionBody::default(), - false, + true, )) .into()], interner, @@ -37,7 +37,7 @@ fn async_function_declaration_keywords() { Some(Sym::YIELD.into()), FormalParameterList::default(), FunctionBody::default(), - false, + true, )) .into()], interner, @@ -50,7 +50,7 @@ fn async_function_declaration_keywords() { Some(Sym::AWAIT.into()), FormalParameterList::default(), FunctionBody::default(), - false, + true, )) .into()], interner, diff --git a/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs b/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs index 0c3443ebcc..628e878be4 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs @@ -111,7 +111,7 @@ where Some(result.0), result.1, result.2, - false, + true, )) } } diff --git a/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs b/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs index aea08f6a0b..a08f7313f0 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs @@ -15,7 +15,7 @@ fn async_generator_function_declaration() { Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), FunctionBody::default(), - false, + true, )) .into()], interner, diff --git a/core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs b/core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs index 21f211e067..adaf266689 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs @@ -81,6 +81,11 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(Function::new(Some(result.0), result.1, result.2)) + Ok(Function::new_with_binding_identifier( + Some(result.0), + result.1, + result.2, + true, + )) } } diff --git a/core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs b/core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs index 5bf2fbdf7f..271b6410a5 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs @@ -12,7 +12,7 @@ fn function_declaration() { let interner = &mut Interner::default(); check_script_parser( "function hello() {}", - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some( interner .get_or_intern_static("hello", utf16!("hello")) @@ -20,6 +20,7 @@ fn function_declaration() { ), FormalParameterList::default(), FunctionBody::default(), + true, )) .into()], interner, @@ -31,7 +32,7 @@ fn function_declaration() { fn function_declaration_keywords() { macro_rules! genast { ($keyword:literal, $interner:expr) => { - vec![Declaration::Function(Function::new( + vec![Declaration::Function(Function::new_with_binding_identifier( Some( $interner .get_or_intern_static($keyword, utf16!($keyword)) @@ -39,6 +40,7 @@ fn function_declaration_keywords() { ), FormalParameterList::default(), FunctionBody::default(), + true, )) .into()] }; diff --git a/core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs b/core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs index 880107f216..911c25529e 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs @@ -88,6 +88,6 @@ where let result = parse_callable_declaration(&self, cursor, interner)?; - Ok(Generator::new(Some(result.0), result.1, result.2, false)) + Ok(Generator::new(Some(result.0), result.1, result.2, true)) } } diff --git a/core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs b/core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs index 215d3f8ba2..69a47ed86b 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs @@ -15,7 +15,7 @@ fn generator_function_declaration() { Some(interner.get_or_intern_static("gen", utf16!("gen")).into()), FormalParameterList::default(), FunctionBody::default(), - false, + true, )) .into()], interner, diff --git a/core/parser/src/parser/statement/mod.rs b/core/parser/src/parser/statement/mod.rs index 9a48b1f07f..e130486b43 100644 --- a/core/parser/src/parser/statement/mod.rs +++ b/core/parser/src/parser/statement/mod.rs @@ -495,7 +495,6 @@ where )?; let mut patterns = Vec::new(); - let mut property_names = Vec::new(); loop { let next_token_is_colon = *cursor.peek(1, interner).or_abrupt()?.kind() @@ -523,10 +522,7 @@ where "object binding pattern", interner, )?; - patterns.push(ObjectPatternElement::RestProperty { - ident, - excluded_keys: property_names, - }); + patterns.push(ObjectPatternElement::RestProperty { ident }); return Ok(patterns); } _ => { @@ -548,9 +544,6 @@ where if is_property_name { let property_name = PropertyName::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - if let Some(name) = property_name.prop_name() { - property_names.push(name.into()); - } cursor.expect( TokenKind::Punctuator(Punctuator::Colon), "object binding pattern", @@ -661,7 +654,6 @@ where } else { let name = BindingIdentifier::new(self.allow_yield, self.allow_await) .parse(cursor, interner)?; - property_names.push(name); match cursor.peek(0, interner)?.map(Token::kind) { Some(TokenKind::Punctuator(Punctuator::Assign)) => { let init = Initializer::new( diff --git a/core/parser/src/parser/statement/throw/mod.rs b/core/parser/src/parser/statement/throw/mod.rs index 40c0f69291..d39276bc4a 100644 --- a/core/parser/src/parser/statement/throw/mod.rs +++ b/core/parser/src/parser/statement/throw/mod.rs @@ -2,11 +2,10 @@ mod tests; use crate::{ - lexer::TokenKind, parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, source::ReadChar, }; -use boa_ast::{statement::Throw, Keyword, Punctuator}; +use boa_ast::{statement::Throw, Keyword}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -52,11 +51,8 @@ where let expr = Expression::new(None, true, self.allow_yield, self.allow_await) .parse(cursor, interner)?; - if let Some(tok) = cursor.peek(0, interner)? { - if tok.kind() == &TokenKind::Punctuator(Punctuator::Semicolon) { - cursor.advance(interner); - } - } + + cursor.expect_semicolon("throw statement", interner)?; Ok(Throw::new(expr)) } diff --git a/core/parser/src/parser/statement/throw/tests.rs b/core/parser/src/parser/statement/throw/tests.rs index 0b922f9a0c..28407e641f 100644 --- a/core/parser/src/parser/statement/throw/tests.rs +++ b/core/parser/src/parser/statement/throw/tests.rs @@ -1,4 +1,4 @@ -use crate::parser::tests::check_script_parser; +use crate::parser::tests::{check_invalid_script, check_script_parser}; use boa_ast::{expression::literal::Literal, statement::Throw, Statement}; use boa_interner::Interner; use boa_macros::utf16; @@ -15,3 +15,8 @@ fn check_throw_parsing() { interner, ); } + +#[test] +fn check_throw_syntax_error() { + check_invalid_script("throw async () => {} - 1;"); +} diff --git a/core/parser/src/parser/tests/format/expression.rs b/core/parser/src/parser/tests/format/expression.rs index 82f3d0f697..4ef4446c11 100644 --- a/core/parser/src/parser/tests/format/expression.rs +++ b/core/parser/src/parser/tests/format/expression.rs @@ -135,3 +135,48 @@ fn object() { "#, ); } + +#[test] +fn array_literal_empty() { + test_formatting( + r" + []; + ", + ); +} + +#[test] +fn array_literal_values() { + test_formatting( + r#" + [0, 1, "a", this, null, undefined, true, false]; + "#, + ); +} + +#[test] +fn array_literal_elision() { + test_formatting( + r" + [, , ,]; + ", + ); +} + +#[test] +fn array_literal_elision_values() { + test_formatting( + r" + [1, 2, , 3, 4]; + ", + ); +} + +#[test] +fn array_literal_elision_start_end() { + test_formatting( + r" + [, , 1, 2, , 3, 4, ,]; + ", + ); +} diff --git a/core/parser/src/parser/tests/mod.rs b/core/parser/src/parser/tests/mod.rs index 89744438cb..56ff4bef7e 100644 --- a/core/parser/src/parser/tests/mod.rs +++ b/core/parser/src/parser/tests/mod.rs @@ -123,13 +123,14 @@ fn hoisting() { UpdateTarget::Identifier(Identifier::new(a)), ))) .into(), - Declaration::Function(Function::new( + Declaration::Function(Function::new_with_binding_identifier( Some(hello.into()), FormalParameterList::default(), FunctionBody::new( vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()] .into(), ), + true, )) .into(), ],