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 5 months ago committed by GitHub
parent
commit
3a6f4a5c68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 68
      core/ast/src/expression/literal/array.rs
  2. 22
      core/ast/src/expression/literal/mod.rs
  3. 103
      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. 6
      core/ast/src/function/async_function.rs
  8. 6
      core/ast/src/function/async_generator.rs
  9. 43
      core/ast/src/function/class.rs
  10. 6
      core/ast/src/function/generator.rs
  11. 6
      core/ast/src/function/mod.rs
  12. 11
      core/ast/src/pattern.rs
  13. 2
      core/ast/src/property.rs
  14. 31
      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

68
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(']');

22
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<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 {
#[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(),

103
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<ObjectPattern> {
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)

40
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<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 {
#[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)));
}

10
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))
}
}

13
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('`');

6
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!(
"({}",

6
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!(
"({}) {}",

43
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",

6
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!(
"({}) {}",

6
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!(
"({}) {}",

11
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<Identifier>,
},
/// `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<Identifier>,
},
/// 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 {

2
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)),
}
}
}

31
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(

35
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,
}

25
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<Identifier>,
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!())
};

4
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));
}

16
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(),
);
}

7
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();

47
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(

42
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(),
)),

105
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 ] = []);");
}

18
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,

6
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(),
],

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)?;
Ok(AsyncFunction::new(
Some(result.0),
result.1,
result.2,
false,
))
Ok(AsyncFunction::new(Some(result.0), result.1, result.2, true))
}
}

6
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,

2
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,
))
}
}

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()),
FormalParameterList::default(),
FunctionBody::default(),
false,
true,
))
.into()],
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)?;
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();
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()]
};

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)?;
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()),
FormalParameterList::default(),
FunctionBody::default(),
false,
true,
))
.into()],
interner,

10
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(

10
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))
}

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_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;");
}

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)),
)))
.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(),
],

Loading…
Cancel
Save