Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Implement arrow function parsing based on CoverParenthesizedExpressionAndArrowParameterList #2171

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions boa_engine/src/bytecompiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,11 @@ impl<'b> ByteCompiler<'b> {
self.emit(Opcode::CopyDataProperties, &[0]);
self.emit_opcode(Opcode::Pop);
}
PropertyDefinition::CoverInitializedName(_, _) => {
return self.context.throw_syntax_error(
"invalid assignment pattern in object literal",
);
}
}
}

Expand Down Expand Up @@ -2025,6 +2030,8 @@ impl<'b> ByteCompiler<'b> {
let env_label = if parameters.has_expressions() {
compiler.code_block.num_bindings = compiler.context.get_binding_number();
compiler.context.push_compile_time_environment(true);
compiler.code_block.function_environment_push_location =
compiler.next_opcode_location();
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment))
} else {
None
Expand Down Expand Up @@ -2665,6 +2672,8 @@ impl<'b> ByteCompiler<'b> {
let env_label = if expr.parameters().has_expressions() {
compiler.code_block.num_bindings = compiler.context.get_binding_number();
compiler.context.push_compile_time_environment(true);
compiler.code_block.function_environment_push_location =
compiler.next_opcode_location();
Some(compiler.emit_opcode_with_two_operands(Opcode::PushFunctionEnvironment))
} else {
None
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/syntax/ast/node/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ impl DeclarationPattern {
#[derive(Clone, Debug, PartialEq)]
pub struct DeclarationPatternObject {
bindings: Vec<BindingPatternTypeObject>,
init: Option<Node>,
pub(crate) init: Option<Node>,
}

impl ToInternedString for DeclarationPatternObject {
Expand Down Expand Up @@ -613,7 +613,7 @@ impl DeclarationPatternObject {
#[derive(Clone, Debug, PartialEq)]
pub struct DeclarationPatternArray {
bindings: Vec<BindingPatternTypeArray>,
init: Option<Node>,
pub(crate) init: Option<Node>,
}

impl ToInternedString for DeclarationPatternArray {
Expand Down
14 changes: 13 additions & 1 deletion boa_engine/src/syntax/ast/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,13 @@ pub enum Node {

/// A call of the super constructor. [More information](./super_call/struct.SuperCall.html).
SuperCall(SuperCall),

/// A FormalParameterList.
///
/// This is only used in the parser itself.
/// It is not a valid AST node.
#[doc(hidden)]
FormalParameterList(FormalParameterList),
raskad marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<Const> for Node {
Expand Down Expand Up @@ -358,6 +365,7 @@ impl Node {
Self::ClassDecl(ref decl) => decl.to_indented_string(interner, indentation),
Self::ClassExpr(ref expr) => expr.to_indented_string(interner, indentation),
Self::SuperCall(ref super_call) => super_call.to_interned_string(interner),
Self::FormalParameterList(_) => unreachable!(),
}
}

Expand Down Expand Up @@ -740,6 +748,7 @@ impl Node {
}
}
},
PropertyDefinition::CoverInitializedName(_, _) => {}
}
}
}
Expand Down Expand Up @@ -1189,7 +1198,6 @@ impl Node {
Node::Object(object) => {
for property in object.properties() {
match property {
PropertyDefinition::IdentifierReference(_) => {}
PropertyDefinition::Property(name, init) => {
if let Some(node) = name.computed() {
if node.contains(symbol) {
Expand All @@ -1212,6 +1220,8 @@ impl Node {
}
}
}
PropertyDefinition::IdentifierReference(_)
| PropertyDefinition::CoverInitializedName(_, _) => {}
}
}
}
Expand All @@ -1237,6 +1247,7 @@ impl Node {
}
}
}
Node::Yield(_) if symbol == ContainsSymbol::YieldExpression => return true,
_ => {}
}
false
Expand All @@ -1248,6 +1259,7 @@ impl Node {
pub(crate) enum ContainsSymbol {
SuperProperty,
SuperCall,
YieldExpression,
}

impl ToInternedString for Node {
Expand Down
15 changes: 15 additions & 0 deletions boa_engine/src/syntax/ast/node/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ impl Object {
},
)
}
PropertyDefinition::CoverInitializedName(ident, expr) => {
format!(
"{indentation}{} = {},\n",
interner.resolve_expect(*ident),
expr.to_no_indent_string(interner, indent_n + 1)
)
}
});
}
buf.push_str(&format!("{}}}", " ".repeat(indent_n)));
Expand Down Expand Up @@ -201,6 +208,14 @@ pub enum PropertyDefinition {
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties
SpreadObject(Node),

/// Cover grammar for when an object literal is used as an object biding pattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName
CoverInitializedName(Sym, Node),
}

impl PropertyDefinition {
Expand Down
98 changes: 93 additions & 5 deletions boa_engine/src/syntax/ast/node/operator/assign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,68 @@ pub(crate) fn object_decl_to_declaration_pattern(
default_init: None,
});
}
(PropertyName::Literal(name), Node::Identifier(ident)) => {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Literal(*name),
default_init: None,
});
}
(PropertyName::Literal(name), Node::Object(object)) => {
let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: PropertyName::Literal(*name),
pattern,
default_init: None,
});
}
(PropertyName::Literal(name), Node::ArrayDecl(array)) => {
let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: PropertyName::Literal(*name),
pattern,
default_init: None,
});
}
(PropertyName::Literal(name), Node::Assign(assign)) => match assign.lhs() {
AssignTarget::Identifier(ident) if *name == ident.sym() => {
if strict && *name == Sym::EVAL {
return None;
}
if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
return None;
}

excluded_keys.push(*name);
bindings.push(BindingPatternTypeObject::SingleName {
ident: *name,
property_name: PropertyName::Literal(*name),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::Identifier(ident) => {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Literal(*name),
default_init: Some(assign.rhs().clone()),
});
}
AssignTarget::DeclarationPattern(pattern) => {
bindings.push(BindingPatternTypeObject::BindingPattern {
ident: PropertyName::Literal(*name),
pattern: pattern.clone(),
default_init: Some(assign.rhs().clone()),
});
}
_ => return None,
},
(PropertyName::Computed(name), Node::Identifier(ident)) => {
bindings.push(BindingPatternTypeObject::SingleName {
ident: ident.sym(),
property_name: PropertyName::Computed(name.clone()),
default_init: None,
});
}
_ => return None,
},
PropertyDefinition::SpreadObject(spread) => {
Expand All @@ -204,6 +266,17 @@ pub(crate) fn object_decl_to_declaration_pattern(
}
}
PropertyDefinition::MethodDefinition(_, _) => return None,
PropertyDefinition::CoverInitializedName(ident, expr) => {
if strict && (*ident == Sym::EVAL || *ident == Sym::ARGUMENTS) {
return None;
}

bindings.push(BindingPatternTypeObject::SingleName {
ident: *ident,
property_name: PropertyName::Literal(*ident),
default_init: Some(expr.clone()),
});
}
}
}
if object.properties().is_empty() {
Expand Down Expand Up @@ -286,11 +359,26 @@ pub(crate) fn array_decl_to_declaration_pattern(
get_field: get_field.clone(),
});
}
AssignTarget::DeclarationPattern(pattern) => {
bindings.push(BindingPatternTypeArray::BindingPattern {
pattern: pattern.clone(),
});
}
AssignTarget::DeclarationPattern(pattern) => match pattern {
DeclarationPattern::Object(pattern) => {
let mut pattern = pattern.clone();
if pattern.init.is_none() {
pattern.init = Some(assign.rhs().clone());
}
bindings.push(BindingPatternTypeArray::BindingPattern {
pattern: DeclarationPattern::Object(pattern),
});
}
DeclarationPattern::Array(pattern) => {
let mut pattern = pattern.clone();
if pattern.init.is_none() {
pattern.init = Some(assign.rhs().clone());
}
bindings.push(BindingPatternTypeArray::BindingPattern {
pattern: DeclarationPattern::Array(pattern),
});
}
},
AssignTarget::GetPrivateField(_) => return None,
},
Node::ArrayDecl(array) => {
Expand Down
70 changes: 67 additions & 3 deletions boa_engine/src/syntax/ast/node/parameters.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::syntax::{ast::Position, parser::ParseError};

use super::{Declaration, DeclarationPattern, Node};
use crate::syntax::{
ast::{
node::{ContainsSymbol, Declaration, DeclarationPattern, Node},
Position,
},
parser::ParseError,
};
use bitflags::bitflags;
use boa_interner::{Interner, Sym, ToInternedString};
use rustc_hash::FxHashSet;

#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -96,6 +101,65 @@ impl FormalParameterList {
}
Ok(())
}

/// Check if the any of the parameters contains a yield expression.
pub(crate) fn contains_yield_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.declaration()
.contains(ContainsSymbol::YieldExpression)
{
return true;
}
}
false
}
}

impl From<Vec<FormalParameter>> for FormalParameterList {
fn from(parameters: Vec<FormalParameter>) -> Self {
let mut flags = FormalParameterListFlags::default();
let mut length = 0;
let mut names = FxHashSet::default();

for parameter in &parameters {
let parameter_names = parameter.names();

for name in parameter_names {
if name == Sym::ARGUMENTS {
flags |= FormalParameterListFlags::HAS_ARGUMENTS;
}
if names.contains(&name) {
flags |= FormalParameterListFlags::HAS_DUPLICATES;
} else {
names.insert(name);
}
}

if parameter.is_rest_param() {
flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
}
if parameter.init().is_some() {
flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
}
if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
{
flags.remove(FormalParameterListFlags::IS_SIMPLE);
}
if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
|| parameter.is_rest_param()
|| parameter.init().is_some())
{
length += 1;
}
}

Self {
parameters: parameters.into_boxed_slice(),
flags,
length,
}
}
}

impl From<FormalParameter> for FormalParameterList {
Expand Down
10 changes: 8 additions & 2 deletions boa_engine/src/syntax/ast/punctuator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl Punctuator {
/// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator
///
/// If there is no match, `None` will be returned.
pub fn as_binop(self) -> Option<BinOp> {
pub const fn as_binop(self) -> Option<BinOp> {
match self {
Self::AssignAdd => Some(BinOp::Assign(AssignOp::Add)),
Self::AssignAnd => Some(BinOp::Assign(AssignOp::And)),
Expand Down Expand Up @@ -186,7 +186,7 @@ impl Punctuator {
}

/// Retrieves the punctuator as a static string.
pub fn as_str(self) -> &'static str {
pub const fn as_str(self) -> &'static str {
match self {
Self::Add => "+",
Self::And => "&",
Expand Down Expand Up @@ -261,3 +261,9 @@ impl Display for Punctuator {
write!(f, "{}", self.as_str())
}
}

impl From<Punctuator> for Box<str> {
fn from(p: Punctuator) -> Self {
p.as_str().into()
}
}
14 changes: 14 additions & 0 deletions boa_engine/src/syntax/parser/cursor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ where
}
}

/// Check if the peeked token is a line terminator.
#[inline]
pub(super) fn peek_expect_is_line_terminator(
&mut self,
skip_n: usize,
interner: &mut Interner,
) -> Result<bool, ParseError> {
if let Some(t) = self.buffered_lexer.peek(skip_n, false, interner)? {
Ok(t.kind() == &TokenKind::LineTerminator)
} else {
Err(ParseError::AbruptEnd)
}
}

/// Advance the cursor to the next token and retrieve it, only if it's of `kind` type.
///
/// When the next token is a `kind` token, get the token, otherwise return `None`.
Expand Down
Loading