Skip to content

Commit

Permalink
Fix various parser idempotency issues and parsing errors (#3917)
Browse files Browse the repository at this point in the history
* Fix various parser idempotency issues and parsing errors

* fix lints
  • Loading branch information
raskad authored Aug 2, 2024
1 parent ec5d6e3 commit 3a6f4a5
Show file tree
Hide file tree
Showing 37 changed files with 469 additions and 312 deletions.
68 changes: 38 additions & 30 deletions core/ast/src/expression/literal/array.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(']');
Expand Down
22 changes: 20 additions & 2 deletions core/ast/src/expression/literal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
Expand Down
103 changes: 52 additions & 51 deletions core/ast/src/expression/literal/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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 => {
Expand All @@ -63,7 +64,6 @@ impl ObjectLiteral {
return None;
}

excluded_keys.push(*ident);
bindings.push(ObjectPatternElement::SingleName {
ident: *ident,
name: PropertyName::Literal(ident.sym()),
Expand All @@ -81,7 +81,6 @@ impl ObjectLiteral {
return None;
}

excluded_keys.push(*ident);
bindings.push(ObjectPatternElement::SingleName {
ident: *ident,
name: PropertyName::Literal(*name),
Expand Down Expand Up @@ -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(),
Expand All @@ -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,
Expand Down Expand Up @@ -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),
Expand All @@ -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)
Expand Down
40 changes: 27 additions & 13 deletions core/ast/src/expression/literal/template.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand All @@ -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 {
Expand Down Expand Up @@ -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)));
}
Expand Down
10 changes: 1 addition & 9 deletions core/ast/src/expression/operator/unary/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand Down
Loading

0 comments on commit 3a6f4a5

Please sign in to comment.