Browse Source

Fix various parser idempotency issues and parsing errors (#3917)

* Fix various parser idempotency issues and parsing errors

* fix lints
pull/3931/head
raskad 4 months ago committed by GitHub
parent
commit
3a6f4a5c68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 28
      core/ast/src/expression/literal/array.rs
  2. 22
      core/ast/src/expression/literal/mod.rs
  3. 49
      core/ast/src/expression/literal/object.rs
  4. 40
      core/ast/src/expression/literal/template.rs
  5. 10
      core/ast/src/expression/operator/unary/mod.rs
  6. 13
      core/ast/src/expression/tagged_template.rs
  7. 2
      core/ast/src/function/async_function.rs
  8. 2
      core/ast/src/function/async_generator.rs
  9. 43
      core/ast/src/function/class.rs
  10. 2
      core/ast/src/function/generator.rs
  11. 2
      core/ast/src/function/mod.rs
  12. 11
      core/ast/src/pattern.rs
  13. 2
      core/ast/src/property.rs
  14. 25
      core/engine/src/bytecompiler/declaration/declaration_pattern.rs
  15. 35
      core/engine/src/bytecompiler/expression/object_literal.rs
  16. 25
      core/engine/src/bytecompiler/mod.rs
  17. 4
      core/engine/src/module/source.rs
  18. 16
      core/parser/src/parser/expression/primary/array_initializer/tests.rs
  19. 7
      core/parser/src/parser/expression/primary/mod.rs
  20. 47
      core/parser/src/parser/expression/primary/object_initializer/mod.rs
  21. 42
      core/parser/src/parser/expression/primary/object_initializer/tests.rs
  22. 105
      core/parser/src/parser/expression/primary/tests.rs
  23. 18
      core/parser/src/parser/function/tests.rs
  24. 6
      core/parser/src/parser/statement/block/tests.rs
  25. 7
      core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  26. 6
      core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs
  27. 2
      core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
  28. 2
      core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
  29. 7
      core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs
  30. 6
      core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs
  31. 2
      core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs
  32. 2
      core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs
  33. 10
      core/parser/src/parser/statement/mod.rs
  34. 10
      core/parser/src/parser/statement/throw/mod.rs
  35. 7
      core/parser/src/parser/statement/throw/tests.rs
  36. 45
      core/parser/src/parser/tests/format/expression.rs
  37. 3
      core/parser/src/parser/tests/mod.rs

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

@ -1,6 +1,6 @@
//! Array declaration Expression. //! Array declaration Expression.
use crate::expression::operator::assign::AssignTarget; use crate::expression::operator::assign::{AssignOp, AssignTarget};
use crate::expression::Expression; use crate::expression::Expression;
use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern}; use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
use crate::try_break; use crate::try_break;
@ -99,7 +99,11 @@ impl ArrayLiteral {
return None; return None;
} }
} }
Expression::Assign(assign) => match assign.lhs() { Expression::Assign(assign) => {
if assign.op() != AssignOp::Assign {
return None;
}
match assign.lhs() {
AssignTarget::Identifier(ident) => { AssignTarget::Identifier(ident) => {
bindings.push(ArrayPatternElement::SingleName { bindings.push(ArrayPatternElement::SingleName {
ident: *ident, ident: *ident,
@ -126,7 +130,8 @@ impl ArrayLiteral {
}); });
} }
}, },
}, }
}
Expression::ArrayLiteral(array) => { Expression::ArrayLiteral(array) => {
let pattern = array.to_pattern(strict)?.into(); let pattern = array.to_pattern(strict)?.into();
bindings.push(ArrayPatternElement::Pattern { bindings.push(ArrayPatternElement::Pattern {
@ -184,15 +189,18 @@ impl ToInternedString for ArrayLiteral {
#[inline] #[inline]
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = String::from("["); let mut buf = String::from("[");
let mut first = true; let mut elements = self.arr.iter().peekable();
for e in &*self.arr {
if first { while let Some(element) = elements.next() {
first = false; if let Some(e) = element {
} else { buf.push_str(&e.to_interned_string(interner));
if elements.peek().is_some() {
buf.push_str(", "); buf.push_str(", ");
} }
if let Some(e) = e { } else if elements.peek().is_some() {
buf.push_str(&e.to_interned_string(interner)); buf.push_str(", ");
} else {
buf.push(',');
} }
} }
buf.push(']'); buf.push(']');

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

@ -33,7 +33,6 @@ use super::Expression;
/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#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 = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Literal { pub enum Literal {
/// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
@ -118,6 +117,25 @@ pub enum Literal {
Undefined, 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<Self> {
let c = <u8 as arbitrary::Arbitrary<'a>>::arbitrary(u)? % 6;
match c {
0 => Ok(Self::String(<Sym as arbitrary::Arbitrary>::arbitrary(u)?)),
1 => Ok(Self::Num(<f64 as arbitrary::Arbitrary>::arbitrary(u)?)),
2 => Ok(Self::Int(<i32 as arbitrary::Arbitrary>::arbitrary(u)?)),
3 => Ok(Self::BigInt(Box::new(
<BigInt as arbitrary::Arbitrary>::arbitrary(u)?,
))),
4 => Ok(Self::Bool(<bool as arbitrary::Arbitrary>::arbitrary(u)?)),
5 => Ok(Self::Null),
_ => unreachable!(),
}
}
}
impl From<Sym> for Literal { impl From<Sym> for Literal {
#[inline] #[inline]
fn from(string: Sym) -> Self { fn from(string: Sym) -> Self {
@ -176,7 +194,7 @@ impl ToInternedString for Literal {
} }
Self::Num(num) => num.to_string(), Self::Num(num) => num.to_string(),
Self::Int(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::Bool(v) => v.to_string(),
Self::Null => "null".to_owned(), Self::Null => "null".to_owned(),
Self::Undefined => "undefined".to_owned(), Self::Undefined => "undefined".to_owned(),

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

@ -2,8 +2,10 @@
use crate::{ use crate::{
block_to_string, block_to_string,
expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT}, expression::{
function::Function, operator::assign::{AssignOp, AssignTarget},
Expression, RESERVED_IDENTIFIERS_STRICT,
},
join_nodes, join_nodes,
pattern::{ObjectPattern, ObjectPatternElement}, pattern::{ObjectPattern, ObjectPatternElement},
property::{MethodDefinition, PropertyDefinition, PropertyName}, property::{MethodDefinition, PropertyDefinition, PropertyName},
@ -52,7 +54,6 @@ impl ObjectLiteral {
#[must_use] #[must_use]
pub fn to_pattern(&self, strict: bool) -> Option<ObjectPattern> { pub fn to_pattern(&self, strict: bool) -> Option<ObjectPattern> {
let mut bindings = Vec::new(); let mut bindings = Vec::new();
let mut excluded_keys = Vec::new();
for (i, property) in self.properties.iter().enumerate() { for (i, property) in self.properties.iter().enumerate() {
match property { match property {
PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => { PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
@ -63,7 +64,6 @@ impl ObjectLiteral {
return None; return None;
} }
excluded_keys.push(*ident);
bindings.push(ObjectPatternElement::SingleName { bindings.push(ObjectPatternElement::SingleName {
ident: *ident, ident: *ident,
name: PropertyName::Literal(ident.sym()), name: PropertyName::Literal(ident.sym()),
@ -81,7 +81,6 @@ impl ObjectLiteral {
return None; return None;
} }
excluded_keys.push(*ident);
bindings.push(ObjectPatternElement::SingleName { bindings.push(ObjectPatternElement::SingleName {
ident: *ident, ident: *ident,
name: PropertyName::Literal(*name), name: PropertyName::Literal(*name),
@ -111,7 +110,11 @@ impl ObjectLiteral {
default_init: None, default_init: None,
}); });
} }
(_, Expression::Assign(assign)) => match assign.lhs() { (_, Expression::Assign(assign)) => {
if assign.op() != AssignOp::Assign {
return None;
}
match assign.lhs() {
AssignTarget::Identifier(ident) => { AssignTarget::Identifier(ident) => {
if let Some(name) = name.literal() { if let Some(name) = name.literal() {
if name == *ident { if name == *ident {
@ -121,7 +124,6 @@ impl ObjectLiteral {
if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) { if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) {
return None; return None;
} }
excluded_keys.push(*ident);
} }
bindings.push(ObjectPatternElement::SingleName { bindings.push(ObjectPatternElement::SingleName {
ident: *ident, ident: *ident,
@ -146,7 +148,8 @@ impl ObjectLiteral {
default_init: Some(assign.rhs().clone()), default_init: Some(assign.rhs().clone()),
}); });
} }
}, }
}
(_, Expression::PropertyAccess(access)) => { (_, Expression::PropertyAccess(access)) => {
bindings.push(ObjectPatternElement::AssignmentPropertyAccess { bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
name: name.clone(), name: name.clone(),
@ -166,15 +169,11 @@ impl ObjectLiteral {
PropertyDefinition::SpreadObject(spread) => { PropertyDefinition::SpreadObject(spread) => {
match spread { match spread {
Expression::Identifier(ident) => { Expression::Identifier(ident) => {
bindings.push(ObjectPatternElement::RestProperty { bindings.push(ObjectPatternElement::RestProperty { ident: *ident });
ident: *ident,
excluded_keys: excluded_keys.clone(),
});
} }
Expression::PropertyAccess(access) => { Expression::PropertyAccess(access) => {
bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess { bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
access: access.clone(), access: access.clone(),
excluded_keys: excluded_keys.clone(),
}); });
} }
_ => return None, _ => return None,
@ -212,12 +211,6 @@ impl ToIndentedString for ObjectLiteral {
format!("{indentation}{},\n", interner.resolve_expect(ident.sym())) format!("{indentation}{},\n", interner.resolve_expect(ident.sym()))
} }
PropertyDefinition::Property(key, value) => { 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!( format!(
"{indentation}{}: {},\n", "{indentation}{}: {},\n",
key.to_interned_string(interner), key.to_interned_string(interner),
@ -229,13 +222,21 @@ impl ToIndentedString for ObjectLiteral {
} }
PropertyDefinition::MethodDefinition(key, method) => { PropertyDefinition::MethodDefinition(key, method) => {
format!( format!(
"{indentation}{}{}({}) {},\n", "{indentation}{}({}) {},\n",
match &method { match &method {
MethodDefinition::Get(_) => "get ", MethodDefinition::Get(_) =>
MethodDefinition::Set(_) => "set ", 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 { match &method {
MethodDefinition::Get(expression) MethodDefinition::Get(expression)
| MethodDefinition::Set(expression) | MethodDefinition::Set(expression)

40
core/ast/src/expression/literal/template.rs

@ -1,16 +1,12 @@
//! Template literal Expression. //! Template literal Expression.
use core::ops::ControlFlow;
use std::borrow::Cow;
use boa_interner::{Interner, Sym, ToInternedString};
use crate::{ use crate::{
expression::Expression, expression::Expression,
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
ToStringEscaped,
}; };
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
/// Template literals are string literals allowing embedded expressions. /// 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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
/// [spec]: https://tc39.es/ecma262/#sec-template-literals /// [spec]: https://tc39.es/ecma262/#sec-template-literals
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct TemplateLiteral { pub struct TemplateLiteral {
elements: Box<[TemplateElement]>, 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<Self> {
let len = u.arbitrary_len::<Box<[TemplateElement]>>()?;
let mut elements = Vec::with_capacity(len);
for i in 0..len {
if i & 1 == 0 {
elements.push(TemplateElement::String(
<Sym as arbitrary::Arbitrary>::arbitrary(u)?,
));
} else {
elements.push(TemplateElement::Expr(Expression::arbitrary(u)?));
}
}
Ok(Self::new(elements.into_boxed_slice()))
}
}
impl From<TemplateLiteral> for Expression { impl From<TemplateLiteral> for Expression {
#[inline] #[inline]
fn from(tem: TemplateLiteral) -> Self { fn from(tem: TemplateLiteral) -> Self {
@ -70,13 +86,11 @@ impl ToInternedString for TemplateLiteral {
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = "`".to_owned(); let mut buf = "`".to_owned();
for elt in &*self.elements { for elt in &self.elements {
match elt { match elt {
TemplateElement::String(s) => buf.push_str(&interner.resolve_expect(*s).join( TemplateElement::String(s) => {
Cow::Borrowed, buf.push_str(&format!("{}", interner.resolve_expect(*s)));
|utf16| Cow::Owned(utf16.to_string_escaped()), }
true,
)),
TemplateElement::Expr(n) => { TemplateElement::Expr(n) => {
buf.push_str(&format!("${{{}}}", n.to_interned_string(interner))); buf.push_str(&format!("${{{}}}", n.to_interned_string(interner)));
} }

10
core/ast/src/expression/operator/unary/mod.rs

@ -72,15 +72,7 @@ impl Unary {
impl ToInternedString for Unary { impl ToInternedString for Unary {
#[inline] #[inline]
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
let space = match self.op { format!("{} {}", self.op, self.target.to_interned_string(interner))
UnaryOp::TypeOf | UnaryOp::Delete | UnaryOp::Void => " ",
_ => "",
};
format!(
"{}{space}{}",
self.op,
self.target.to_interned_string(interner)
)
} }
} }

13
core/ast/src/expression/tagged_template.rs

@ -85,12 +85,13 @@ impl ToInternedString for TaggedTemplate {
#[inline] #[inline]
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = format!("{}`", self.tag.to_interned_string(interner)); let mut buf = format!("{}`", self.tag.to_interned_string(interner));
for (&raw, expr) in self.raws.iter().zip(self.exprs.iter()) { let mut exprs = self.exprs.iter();
buf.push_str(&format!(
"{}${{{}}}", for raw in &self.raws {
interner.resolve_expect(raw), buf.push_str(&format!("{}", interner.resolve_expect(*raw)));
expr.to_interned_string(interner) if let Some(expr) = exprs.next() {
)); buf.push_str(&format!("${{{}}}", expr.to_interned_string(interner)));
}
} }
buf.push('`'); buf.push('`');

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

@ -79,9 +79,11 @@ impl AsyncFunction {
impl ToIndentedString for AsyncFunction { impl ToIndentedString for AsyncFunction {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "async function".to_owned(); let mut buf = "async function".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name { if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
} }
}
buf.push_str(&format!( buf.push_str(&format!(
"({}", "({}",
join_nodes(interner, self.parameters.as_ref()) join_nodes(interner, self.parameters.as_ref())

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

@ -78,9 +78,11 @@ impl AsyncGenerator {
impl ToIndentedString for AsyncGenerator { impl ToIndentedString for AsyncGenerator {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "async function*".to_owned(); let mut buf = "async function*".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name { if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
} }
}
buf.push_str(&format!( buf.push_str(&format!(
"({}) {}", "({}) {}",
join_nodes(interner, self.parameters.as_ref()), join_nodes(interner, self.parameters.as_ref()),

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

@ -6,11 +6,10 @@ use crate::{
property::{MethodDefinition, PropertyName}, property::{MethodDefinition, PropertyName},
try_break, try_break,
visitor::{VisitWith, Visitor, VisitorMut}, visitor::{VisitWith, Visitor, VisitorMut},
Declaration, ToStringEscaped, Declaration,
}; };
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use std::borrow::Cow;
use std::hash::Hash; use std::hash::Hash;
/// A class declaration, as defined by the [spec]. /// A class declaration, as defined by the [spec].
@ -89,34 +88,24 @@ impl Class {
impl ToIndentedString for Class { impl ToIndentedString for Class {
fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String { fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
let class_name = self.name.map_or(Cow::Borrowed(""), |s| { let mut buf = "class".to_string();
interner.resolve_expect(s.sym()).join( if self.has_binding_identifier {
Cow::Borrowed, if let Some(name) = self.name {
|utf16| Cow::Owned(utf16.to_string_escaped()), buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
true, }
) }
}); if let Some(super_ref) = self.super_ref.as_ref() {
if self.elements.is_empty() && self.constructor().is_none() { buf.push_str(&format!(
return format!(
"class {class_name}{} {{}}",
self.super_ref
.as_ref()
.map_or_else(String::new, |sup| format!(
" extends {}", " extends {}",
sup.to_interned_string(interner) super_ref.to_interned_string(interner)
)) ));
); }
if self.elements.is_empty() && self.constructor().is_none() {
buf.push_str(" {}");
return buf;
} }
let indentation = " ".repeat(indent_n + 1); let indentation = " ".repeat(indent_n + 1);
let mut buf = format!( buf.push_str(" {\n");
"class {class_name}{} {{\n",
self.super_ref
.as_ref()
.map_or_else(String::new, |sup| format!(
"extends {}",
sup.to_interned_string(interner)
))
);
if let Some(expr) = &self.constructor { if let Some(expr) = &self.constructor {
buf.push_str(&format!( buf.push_str(&format!(
"{indentation}constructor({}) {}\n", "{indentation}constructor({}) {}\n",

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

@ -80,9 +80,11 @@ impl Generator {
impl ToIndentedString for Generator { impl ToIndentedString for Generator {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "function*".to_owned(); let mut buf = "function*".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name { if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
} }
}
buf.push_str(&format!( buf.push_str(&format!(
"({}) {}", "({}) {}",
join_nodes(interner, self.parameters.as_ref()), join_nodes(interner, self.parameters.as_ref()),

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

@ -132,9 +132,11 @@ impl Function {
impl ToIndentedString for Function { impl ToIndentedString for Function {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = "function".to_owned(); let mut buf = "function".to_owned();
if self.has_binding_identifier {
if let Some(name) = self.name { if let Some(name) = self.name {
buf.push_str(&format!(" {}", interner.resolve_expect(name.sym()))); buf.push_str(&format!(" {}", interner.resolve_expect(name.sym())));
} }
}
buf.push_str(&format!( buf.push_str(&format!(
"({}) {}", "({}) {}",
join_nodes(interner, self.parameters.as_ref()), join_nodes(interner, self.parameters.as_ref()),

11
core/ast/src/pattern.rs

@ -308,8 +308,6 @@ pub enum ObjectPatternElement {
RestProperty { RestProperty {
/// The variable name where the unassigned properties will be stored. /// The variable name where the unassigned properties will be stored.
ident: Identifier, ident: Identifier,
/// A list of the excluded property keys that were already destructured.
excluded_keys: Vec<Identifier>,
}, },
/// `AssignmentGetField` represents an `AssignmentProperty` with an expression field member expression `AssignmentElement`. /// `AssignmentGetField` represents an `AssignmentProperty` with an expression field member expression `AssignmentElement`.
@ -342,8 +340,6 @@ pub enum ObjectPatternElement {
AssignmentRestPropertyAccess { AssignmentRestPropertyAccess {
/// The property access where the unassigned properties will be stored. /// The property access where the unassigned properties will be stored.
access: PropertyAccess, access: PropertyAccess,
/// A list of the excluded property keys that were already destructured.
excluded_keys: Vec<Identifier>,
}, },
/// Pattern represents a property with a `Pattern` as the element. /// Pattern represents a property with a `Pattern` as the element.
@ -397,13 +393,10 @@ impl ToInternedString for ObjectPatternElement {
} }
buf buf
} }
Self::RestProperty { Self::RestProperty { ident } => {
ident,
excluded_keys: _,
} => {
format!(" ... {}", interner.resolve_expect(ident.sym())) format!(" ... {}", interner.resolve_expect(ident.sym()))
} }
Self::AssignmentRestPropertyAccess { access, .. } => { Self::AssignmentRestPropertyAccess { access } => {
format!(" ... {}", access.to_interned_string(interner)) format!(" ... {}", access.to_interned_string(interner))
} }
Self::AssignmentPropertyAccess { Self::AssignmentPropertyAccess {

2
core/ast/src/property.rs

@ -304,7 +304,7 @@ impl ToInternedString for PropertyName {
fn to_interned_string(&self, interner: &Interner) -> String { fn to_interned_string(&self, interner: &Interner) -> String {
match self { match self {
Self::Literal(key) => interner.resolve_expect(*key).to_string(), 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)),
} }
} }
} }

25
core/engine/src/bytecompiler/declaration/declaration_pattern.rs

@ -19,6 +19,7 @@ impl ByteCompiler<'_> {
self.emit_opcode(Opcode::RequireObjectCoercible); self.emit_opcode(Opcode::RequireObjectCoercible);
let mut excluded_keys = Vec::new();
let mut additional_excluded_keys_count = 0; let mut additional_excluded_keys_count = 0;
let rest_exits = pattern.has_rest(); let rest_exits = pattern.has_rest();
@ -40,6 +41,7 @@ impl ByteCompiler<'_> {
match name { match name {
PropertyName::Literal(name) => { PropertyName::Literal(name) => {
self.emit_get_property_by_name(*name); self.emit_get_property_by_name(*name);
excluded_keys.push(*name);
} }
PropertyName::Computed(node) => { PropertyName::Computed(node) => {
self.compile_expr(node, true); self.compile_expr(node, true);
@ -65,15 +67,12 @@ impl ByteCompiler<'_> {
} }
} }
// BindingRestProperty : ... BindingIdentifier // BindingRestProperty : ... BindingIdentifier
RestProperty { RestProperty { ident } => {
ident,
excluded_keys,
} => {
self.emit_opcode(Opcode::PushEmptyObject); self.emit_opcode(Opcode::PushEmptyObject);
for key in excluded_keys { for key in &excluded_keys {
self.emit_push_literal(Literal::String( 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())); self.emit_binding(def, ident.to_js_string(self.interner()));
} }
AssignmentRestPropertyAccess { AssignmentRestPropertyAccess { access } => {
access,
excluded_keys,
} => {
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::PushEmptyObject); self.emit_opcode(Opcode::PushEmptyObject);
for key in excluded_keys { for key in &excluded_keys {
self.emit_push_literal(Literal::String( 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( self.emit(
@ -117,11 +113,14 @@ impl ByteCompiler<'_> {
} => { } => {
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
if let PropertyName::Computed(node) = &name { match &name {
PropertyName::Literal(name) => excluded_keys.push(*name),
PropertyName::Computed(node) => {
self.compile_expr(node, true); self.compile_expr(node, true);
self.emit_opcode(Opcode::ToPropertyKey); self.emit_opcode(Opcode::ToPropertyKey);
self.emit_opcode(Opcode::Swap); self.emit_opcode(Opcode::Swap);
} }
}
self.access_set( self.access_set(
Access::Property { access }, Access::Property { access },

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

@ -1,5 +1,5 @@
use crate::{ use crate::{
bytecompiler::{Access, ByteCompiler, FunctionSpec, Operand}, bytecompiler::{Access, ByteCompiler, FunctionSpec, MethodKind, Operand},
vm::Opcode, vm::Opcode,
}; };
use boa_ast::{ use boa_ast::{
@ -46,7 +46,9 @@ impl ByteCompiler<'_> {
PropertyDefinition::MethodDefinition(name, kind) => match kind { PropertyDefinition::MethodDefinition(name, kind) => match kind {
MethodDefinition::Get(expr) => match name { MethodDefinition::Get(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::SetPropertyGetterByName, index); self.emit_with_varying_operand(Opcode::SetPropertyGetterByName, index);
@ -61,7 +63,9 @@ impl ByteCompiler<'_> {
}, },
MethodDefinition::Set(expr) => match name { MethodDefinition::Set(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::SetPropertySetterByName, index); self.emit_with_varying_operand(Opcode::SetPropertySetterByName, index);
@ -76,7 +80,9 @@ impl ByteCompiler<'_> {
}, },
MethodDefinition::Ordinary(expr) => match name { MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
@ -91,7 +97,9 @@ impl ByteCompiler<'_> {
}, },
MethodDefinition::Async(expr) => match name { MethodDefinition::Async(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
@ -106,7 +114,9 @@ impl ByteCompiler<'_> {
}, },
MethodDefinition::Generator(expr) => match name { MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
@ -121,7 +131,9 @@ impl ByteCompiler<'_> {
}, },
MethodDefinition::AsyncGenerator(expr) => match name { MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(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); self.emit_opcode(Opcode::SetHomeObject);
let index = self.get_or_insert_name((*name).into()); let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index);
@ -171,7 +183,7 @@ impl ByteCompiler<'_> {
self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup);
// stack: object, object, ToPropertyKey(name), ToPropertyKey(name) // stack: object, object, ToPropertyKey(name), ToPropertyKey(name)
self.object_method(function); self.object_method(function, kind);
// stack: object, object, ToPropertyKey(name), ToPropertyKey(name), method // stack: object, object, ToPropertyKey(name), ToPropertyKey(name), method
let value = match kind { let value = match kind {
@ -204,10 +216,3 @@ impl ByteCompiler<'_> {
} }
} }
} }
#[derive(Debug, Clone, Copy)]
enum MethodKind {
Get,
Set,
Ordinary,
}

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

@ -40,6 +40,7 @@ use boa_ast::{
}; };
use boa_gc::Gc; use boa_gc::Gc;
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_macros::js_str;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use thin_vec::ThinVec; use thin_vec::ThinVec;
@ -112,7 +113,7 @@ pub(crate) struct FunctionSpec<'a> {
pub(crate) name: Option<Identifier>, pub(crate) name: Option<Identifier>,
parameters: &'a FormalParameterList, parameters: &'a FormalParameterList,
body: &'a FunctionBody, body: &'a FunctionBody,
has_binding_identifier: bool, pub(crate) has_binding_identifier: bool,
} }
impl<'a> From<&'a Function> for FunctionSpec<'a> { 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()` /// Represents a callable expression, like `f()` or `new Cl()`
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum Callable<'a> { enum Callable<'a> {
@ -1346,12 +1354,16 @@ impl<'ctx> ByteCompiler<'ctx> {
/// pushing it to the stack if necessary. /// pushing it to the stack if necessary.
pub(crate) fn function_with_binding( pub(crate) fn function_with_binding(
&mut self, &mut self,
function: FunctionSpec<'_>, mut function: FunctionSpec<'_>,
node_kind: NodeKind, node_kind: NodeKind,
use_expr: bool, use_expr: bool,
) { ) {
let name = function.name; let name = function.name;
if node_kind == NodeKind::Declaration {
function.has_binding_identifier = false;
}
let index = self.function(function); let index = self.function(function);
self.emit_with_varying_operand(Opcode::GetFunction, index); self.emit_with_varying_operand(Opcode::GetFunction, index);
@ -1372,7 +1384,7 @@ impl<'ctx> ByteCompiler<'ctx> {
} }
/// Compile an object method AST Node into bytecode. /// 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) = ( let (generator, r#async, arrow) = (
function.kind.is_generator(), function.kind.is_generator(),
function.kind.is_async(), function.kind.is_async(),
@ -1387,7 +1399,12 @@ impl<'ctx> ByteCompiler<'ctx> {
} = function; } = function;
let name = if let Some(name) = name { 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 { } else {
Some(js_string!()) Some(js_string!())
}; };

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

@ -1548,7 +1548,7 @@ impl SourceTextModule {
// 2. Perform ! env.InitializeBinding(dn, fo). // 2. Perform ! env.InitializeBinding(dn, fo).
// //
// deferred to below. // deferred to below.
let (spec, locator): (FunctionSpec<'_>, _) = match declaration { let (mut spec, locator): (FunctionSpec<'_>, _) = match declaration {
LexicallyScopedDeclaration::Function(f) => { LexicallyScopedDeclaration::Function(f) => {
let name = bound_names(f)[0].to_js_string(compiler.interner()); let name = bound_names(f)[0].to_js_string(compiler.interner());
let locator = env.create_mutable_binding(name, false); let locator = env.create_mutable_binding(name, false);
@ -1608,6 +1608,8 @@ impl SourceTextModule {
} }
}; };
spec.has_binding_identifier = false;
functions.push((spec, locator)); functions.push((spec, locator));
} }

16
core/parser/src/parser/expression/primary/array_initializer/tests.rs

@ -133,3 +133,19 @@ fn check_combined_empty_str() {
&mut Interner::default(), &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(),
);
}

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

@ -47,7 +47,7 @@ use boa_ast::{
self as ast, self as ast,
declaration::Variable, declaration::Variable,
expression::{ expression::{
literal::Literal, literal::{self, Literal, TemplateElement},
operator::{assign::AssignTarget, binary::BinaryOp}, operator::{assign::AssignTarget, binary::BinaryOp},
Identifier, Parenthesized, Identifier, Parenthesized,
}, },
@ -221,9 +221,10 @@ where
tok.span().start(), tok.span().start(),
)); ));
}; };
let node = Literal::from(cooked).into(); let temp =
literal::TemplateLiteral::new(Box::new([TemplateElement::String(cooked)]));
cursor.advance(interner); cursor.advance(interner);
Ok(node) Ok(temp.into())
} }
TokenKind::NumericLiteral(Numeric::Integer(num)) => { TokenKind::NumericLiteral(Numeric::Integer(num)) => {
let node = Literal::from(*num).into(); let node = Literal::from(*num).into();

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

@ -39,7 +39,6 @@ use boa_ast::{
Expression, Keyword, Punctuator, Expression, Keyword, Punctuator,
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
/// Parses an object literal. /// Parses an object literal.
@ -357,15 +356,8 @@ where
interner, 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( let method = MethodDefinition::Get(Function::new(
name, None,
FormalParameterList::default(), FormalParameterList::default(),
body, body,
)); ));
@ -450,14 +442,7 @@ where
interner, interner,
)?; )?;
let name = property_name.literal().map(|name| { let method = MethodDefinition::Set(Function::new(None, parameters, body));
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));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
// https://tc39.es/ecma262/#sec-object-initializer-static-semantics-early-errors // https://tc39.es/ecma262/#sec-object-initializer-static-semantics-early-errors
@ -526,11 +511,7 @@ where
interner, interner,
)?; )?;
let method = MethodDefinition::Ordinary(Function::new( let method = MethodDefinition::Ordinary(Function::new(None, params, body));
property_name.literal().map(Into::into),
params,
body,
));
// It is a Syntax Error if HasDirectSuper of MethodDefinition is true. // It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
if has_direct_super(&method) { if has_direct_super(&method) {
@ -809,12 +790,7 @@ where
interner, interner,
)?; )?;
let method = MethodDefinition::Generator(Generator::new( let method = MethodDefinition::Generator(Generator::new(None, params, body, false));
class_element_name.literal().map(Into::into),
params,
body,
false,
));
if contains(&method, ContainsSymbol::Super) { if contains(&method, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax( return Err(Error::lex(LexError::Syntax(
@ -925,12 +901,8 @@ where
interner, interner,
)?; )?;
let method = MethodDefinition::AsyncGenerator(AsyncGenerator::new( let method =
name.literal().map(Into::into), MethodDefinition::AsyncGenerator(AsyncGenerator::new(None, params, body, false));
params,
body,
false,
));
if contains(&method, ContainsSymbol::Super) { if contains(&method, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax( return Err(Error::lex(LexError::Syntax(
@ -1018,12 +990,7 @@ where
interner, interner,
)?; )?;
let method = MethodDefinition::Async(AsyncFunction::new( let method = MethodDefinition::Async(AsyncFunction::new(None, params, body, false));
class_element_name.literal().map(Into::into),
params,
body,
false,
));
if contains(&method, ContainsSymbol::Super) { if contains(&method, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax( return Err(Error::lex(LexError::Syntax(

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

@ -63,7 +63,7 @@ fn check_object_short_function() {
PropertyDefinition::MethodDefinition( PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(), interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Ordinary(Function::new( MethodDefinition::Ordinary(Function::new(
Some(interner.get_or_intern_static("b", utf16!("b")).into()), None,
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
)), )),
@ -112,11 +112,7 @@ fn check_object_short_function_arguments() {
), ),
PropertyDefinition::MethodDefinition( PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(), interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Ordinary(Function::new( MethodDefinition::Ordinary(Function::new(None, parameters, FunctionBody::default())),
Some(interner.get_or_intern_static("b", utf16!("b")).into()),
parameters,
FunctionBody::default(),
)),
), ),
]; ];
@ -151,11 +147,7 @@ fn check_object_getter() {
PropertyDefinition::MethodDefinition( PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(), interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Get(Function::new( MethodDefinition::Get(Function::new(
Some( None,
interner
.get_or_intern_static("get b", utf16!("get b"))
.into(),
),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
)), )),
@ -203,15 +195,7 @@ fn check_object_setter() {
), ),
PropertyDefinition::MethodDefinition( PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(), interner.get_or_intern_static("b", utf16!("b")).into(),
MethodDefinition::Set(Function::new( MethodDefinition::Set(Function::new(None, params, FunctionBody::default())),
Some(
interner
.get_or_intern_static("set b", utf16!("set b"))
.into(),
),
params,
FunctionBody::default(),
)),
), ),
]; ];
@ -241,7 +225,7 @@ fn check_object_short_function_get() {
let object_properties = vec![PropertyDefinition::MethodDefinition( let object_properties = vec![PropertyDefinition::MethodDefinition(
Sym::GET.into(), Sym::GET.into(),
MethodDefinition::Ordinary(Function::new( MethodDefinition::Ordinary(Function::new(
Some(interner.get_or_intern_static("get", utf16!("get")).into()), None,
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
)), )),
@ -272,7 +256,7 @@ fn check_object_short_function_set() {
let object_properties = vec![PropertyDefinition::MethodDefinition( let object_properties = vec![PropertyDefinition::MethodDefinition(
Sym::SET.into(), Sym::SET.into(),
MethodDefinition::Ordinary(Function::new( MethodDefinition::Ordinary(Function::new(
Some(interner.get_or_intern_static("set", utf16!("set")).into()), None,
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
)), )),
@ -420,7 +404,7 @@ fn check_async_method() {
let object_properties = vec![PropertyDefinition::MethodDefinition( let object_properties = vec![PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("dive", utf16!("dive")).into(), interner.get_or_intern_static("dive", utf16!("dive")).into(),
MethodDefinition::Async(AsyncFunction::new( MethodDefinition::Async(AsyncFunction::new(
Some(interner.get_or_intern_static("dive", utf16!("dive")).into()), None,
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, false,
@ -454,11 +438,7 @@ fn check_async_generator_method() {
.get_or_intern_static("vroom", utf16!("vroom")) .get_or_intern_static("vroom", utf16!("vroom"))
.into(), .into(),
MethodDefinition::AsyncGenerator(AsyncGenerator::new( MethodDefinition::AsyncGenerator(AsyncGenerator::new(
Some( None,
interner
.get_or_intern_static("vroom", utf16!("vroom"))
.into(),
),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, false,
@ -512,11 +492,7 @@ fn check_async_ordinary_method() {
let object_properties = vec![PropertyDefinition::MethodDefinition( let object_properties = vec![PropertyDefinition::MethodDefinition(
PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))), PropertyName::Literal(interner.get_or_intern_static("async", utf16!("async"))),
MethodDefinition::Ordinary(Function::new( MethodDefinition::Ordinary(Function::new(
Some( None,
interner
.get_or_intern_static("async", utf16!("async"))
.into(),
),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
)), )),

105
core/parser/src/parser/expression/primary/tests.rs

@ -1,5 +1,17 @@
use crate::parser::tests::check_script_parser; use crate::parser::tests::{check_invalid_script, check_script_parser};
use boa_ast::{expression::literal::Literal, Expression, Statement}; 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_interner::{Interner, Sym};
use boa_macros::utf16; use boa_macros::utf16;
@ -23,3 +35,92 @@ fn check_string() {
interner, 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 ] = []);");
}

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

@ -28,7 +28,7 @@ fn check_basic() {
check_script_parser( check_script_parser(
"function foo(a) { return a; }", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::new( FunctionBody::new(
@ -39,6 +39,7 @@ fn check_basic() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into()], .into()],
interner, interner,
@ -66,7 +67,7 @@ fn check_duplicates_strict_off() {
assert_eq!(params.length(), 2); assert_eq!(params.length(), 2);
check_script_parser( check_script_parser(
"function foo(a, a) { return a; }", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::new( FunctionBody::new(
@ -77,6 +78,7 @@ fn check_duplicates_strict_off() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into()], .into()],
interner, interner,
@ -102,7 +104,7 @@ fn check_basic_semicolon_insertion() {
check_script_parser( check_script_parser(
"function foo(a) { return a }", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::new( FunctionBody::new(
@ -113,6 +115,7 @@ fn check_basic_semicolon_insertion() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into()], .into()],
interner, interner,
@ -131,7 +134,7 @@ fn check_empty_return() {
assert_eq!(params.length(), 1); assert_eq!(params.length(), 1);
check_script_parser( check_script_parser(
"function foo(a) { return; }", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::new( FunctionBody::new(
@ -140,6 +143,7 @@ fn check_empty_return() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into()], .into()],
interner, interner,
@ -158,7 +162,7 @@ fn check_empty_return_semicolon_insertion() {
assert_eq!(params.length(), 1); assert_eq!(params.length(), 1);
check_script_parser( check_script_parser(
"function foo(a) { return }", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::new( FunctionBody::new(
@ -167,6 +171,7 @@ fn check_empty_return_semicolon_insertion() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into()], .into()],
interner, interner,
@ -194,10 +199,11 @@ fn check_rest_operator() {
assert_eq!(params.length(), 1); assert_eq!(params.length(), 1);
check_script_parser( check_script_parser(
"function foo(a, ...b) {}", "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()), Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
params, params,
FunctionBody::default(), FunctionBody::default(),
true,
)) ))
.into()], .into()],
interner, interner,

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

@ -78,7 +78,7 @@ fn non_empty() {
a++; a++;
}", }",
vec![ vec![
Declaration::Function(Function::new( Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()), Some(hello.into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::new( FunctionBody::new(
@ -87,6 +87,7 @@ fn non_empty() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into(), .into(),
Statement::Var(VarDeclaration( Statement::Var(VarDeclaration(
@ -135,7 +136,7 @@ fn hoisting() {
UpdateTarget::Identifier(Identifier::new(a)), UpdateTarget::Identifier(Identifier::new(a)),
))) )))
.into(), .into(),
Declaration::Function(Function::new( Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()), Some(hello.into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::new( FunctionBody::new(
@ -144,6 +145,7 @@ fn hoisting() {
))] ))]
.into(), .into(),
), ),
true,
)) ))
.into(), .into(),
], ],

7
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)?; let result = parse_callable_declaration(&self, cursor, interner)?;
Ok(AsyncFunction::new( Ok(AsyncFunction::new(Some(result.0), result.1, result.2, true))
Some(result.0),
result.1,
result.2,
false,
))
} }
} }

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

@ -20,7 +20,7 @@ fn async_function_declaration() {
), ),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, true,
)) ))
.into()], .into()],
interner, interner,
@ -37,7 +37,7 @@ fn async_function_declaration_keywords() {
Some(Sym::YIELD.into()), Some(Sym::YIELD.into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, true,
)) ))
.into()], .into()],
interner, interner,
@ -50,7 +50,7 @@ fn async_function_declaration_keywords() {
Some(Sym::AWAIT.into()), Some(Sym::AWAIT.into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, true,
)) ))
.into()], .into()],
interner, interner,

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

@ -111,7 +111,7 @@ where
Some(result.0), Some(result.0),
result.1, result.1,
result.2, result.2,
false, true,
)) ))
} }
} }

2
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()), Some(interner.get_or_intern_static("gen", utf16!("gen")).into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, true,
)) ))
.into()], .into()],
interner, interner,

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

@ -81,6 +81,11 @@ where
let result = parse_callable_declaration(&self, cursor, interner)?; 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,
))
} }
} }

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

@ -12,7 +12,7 @@ fn function_declaration() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
check_script_parser( check_script_parser(
"function hello() {}", "function hello() {}",
vec![Declaration::Function(Function::new( vec![Declaration::Function(Function::new_with_binding_identifier(
Some( Some(
interner interner
.get_or_intern_static("hello", utf16!("hello")) .get_or_intern_static("hello", utf16!("hello"))
@ -20,6 +20,7 @@ fn function_declaration() {
), ),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
true,
)) ))
.into()], .into()],
interner, interner,
@ -31,7 +32,7 @@ fn function_declaration() {
fn function_declaration_keywords() { fn function_declaration_keywords() {
macro_rules! genast { macro_rules! genast {
($keyword:literal, $interner:expr) => { ($keyword:literal, $interner:expr) => {
vec![Declaration::Function(Function::new( vec![Declaration::Function(Function::new_with_binding_identifier(
Some( Some(
$interner $interner
.get_or_intern_static($keyword, utf16!($keyword)) .get_or_intern_static($keyword, utf16!($keyword))
@ -39,6 +40,7 @@ fn function_declaration_keywords() {
), ),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
true,
)) ))
.into()] .into()]
}; };

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

@ -88,6 +88,6 @@ where
let result = parse_callable_declaration(&self, cursor, interner)?; 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))
} }
} }

2
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()), Some(interner.get_or_intern_static("gen", utf16!("gen")).into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::default(), FunctionBody::default(),
false, true,
)) ))
.into()], .into()],
interner, interner,

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

@ -495,7 +495,6 @@ where
)?; )?;
let mut patterns = Vec::new(); let mut patterns = Vec::new();
let mut property_names = Vec::new();
loop { loop {
let next_token_is_colon = *cursor.peek(1, interner).or_abrupt()?.kind() let next_token_is_colon = *cursor.peek(1, interner).or_abrupt()?.kind()
@ -523,10 +522,7 @@ where
"object binding pattern", "object binding pattern",
interner, interner,
)?; )?;
patterns.push(ObjectPatternElement::RestProperty { patterns.push(ObjectPatternElement::RestProperty { ident });
ident,
excluded_keys: property_names,
});
return Ok(patterns); return Ok(patterns);
} }
_ => { _ => {
@ -548,9 +544,6 @@ where
if is_property_name { if is_property_name {
let property_name = PropertyName::new(self.allow_yield, self.allow_await) let property_name = PropertyName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
if let Some(name) = property_name.prop_name() {
property_names.push(name.into());
}
cursor.expect( cursor.expect(
TokenKind::Punctuator(Punctuator::Colon), TokenKind::Punctuator(Punctuator::Colon),
"object binding pattern", "object binding pattern",
@ -661,7 +654,6 @@ where
} else { } else {
let name = BindingIdentifier::new(self.allow_yield, self.allow_await) let name = BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
property_names.push(name);
match cursor.peek(0, interner)?.map(Token::kind) { match cursor.peek(0, interner)?.map(Token::kind) {
Some(TokenKind::Punctuator(Punctuator::Assign)) => { Some(TokenKind::Punctuator(Punctuator::Assign)) => {
let init = Initializer::new( let init = Initializer::new(

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

@ -2,11 +2,10 @@
mod tests; mod tests;
use crate::{ use crate::{
lexer::TokenKind,
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
source::ReadChar, source::ReadChar,
}; };
use boa_ast::{statement::Throw, Keyword, Punctuator}; use boa_ast::{statement::Throw, Keyword};
use boa_interner::Interner; use boa_interner::Interner;
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -52,11 +51,8 @@ where
let expr = Expression::new(None, true, self.allow_yield, self.allow_await) let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
if let Some(tok) = cursor.peek(0, interner)? {
if tok.kind() == &TokenKind::Punctuator(Punctuator::Semicolon) { cursor.expect_semicolon("throw statement", interner)?;
cursor.advance(interner);
}
}
Ok(Throw::new(expr)) Ok(Throw::new(expr))
} }

7
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_ast::{expression::literal::Literal, statement::Throw, Statement};
use boa_interner::Interner; use boa_interner::Interner;
use boa_macros::utf16; use boa_macros::utf16;
@ -15,3 +15,8 @@ fn check_throw_parsing() {
interner, interner,
); );
} }
#[test]
fn check_throw_syntax_error() {
check_invalid_script("throw async () => {} - 1;");
}

45
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, ,];
",
);
}

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

@ -123,13 +123,14 @@ fn hoisting() {
UpdateTarget::Identifier(Identifier::new(a)), UpdateTarget::Identifier(Identifier::new(a)),
))) )))
.into(), .into(),
Declaration::Function(Function::new( Declaration::Function(Function::new_with_binding_identifier(
Some(hello.into()), Some(hello.into()),
FormalParameterList::default(), FormalParameterList::default(),
FunctionBody::new( FunctionBody::new(
vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()] vec![Statement::Return(Return::new(Some(Literal::from(10).into()))).into()]
.into(), .into(),
), ),
true,
)) ))
.into(), .into(),
], ],

Loading…
Cancel
Save