summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--js/src/frontend/Parser.cpp398
-rw-r--r--js/src/frontend/Parser.h30
2 files changed, 331 insertions, 97 deletions
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 3154e41d5..85f7ee846 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4243,7 +4243,7 @@ Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other)
template <>
bool
-Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl)
+Parser<FullParseHandler>::checkDestructuringAssignmentName(ParseNode* expr)
{
MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
@@ -4255,24 +4255,8 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat
return false;
}
- // This expression might be in a variable-binding pattern where only plain,
- // unparenthesized names are permitted.
- if (maybeDecl) {
- // Destructuring patterns in declarations must only contain
- // unparenthesized names.
- if (!handler.isUnparenthesizedName(expr)) {
- errorAt(expr->pn_pos.begin, JSMSG_NO_VARIABLE_NAME);
- return false;
- }
-
- RootedPropertyName name(context, expr->name());
- // `yield` is already checked, so pass YieldIsName to skip that check.
- if (!checkBindingIdentifier(name, expr->pn_pos.begin, YieldIsName))
- return false;
- return noteDeclaredName(name, *maybeDecl, expr->pn_pos);
- }
-
- // Otherwise this is an expression in destructuring outside a declaration.
+ // The expression must be a simple assignment target, i.e. either a name
+ // or a property accessor.
if (handler.isNameAnyParentheses(expr)) {
if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(expr, context)) {
if (!strictModeErrorAt(expr->pn_pos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
@@ -4291,14 +4275,12 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat
template <>
bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */);
+Parser<FullParseHandler>::checkDestructuringAssignmentPattern(ParseNode* pattern,
+ PossibleError* possibleError /* = nullptr */);
template <>
bool
-Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
- Maybe<DeclarationKind> maybeDecl)
+Parser<FullParseHandler>::checkDestructuringAssignmentObject(ParseNode* objectPattern)
{
MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
@@ -4319,10 +4301,10 @@ Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
target = target->pn_left;
if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (!checkDestructuringPattern(target, maybeDecl))
+ if (!checkDestructuringAssignmentPattern(target))
return false;
} else {
- if (!checkDestructuringName(target, maybeDecl))
+ if (!checkDestructuringAssignmentName(target))
return false;
}
}
@@ -4332,8 +4314,7 @@ Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
template <>
bool
-Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
- Maybe<DeclarationKind> maybeDecl)
+Parser<FullParseHandler>::checkDestructuringAssignmentArray(ParseNode* arrayPattern)
{
MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
@@ -4355,10 +4336,10 @@ Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
}
if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (!checkDestructuringPattern(target, maybeDecl))
+ if (!checkDestructuringAssignmentPattern(target))
return false;
} else {
- if (!checkDestructuringName(target, maybeDecl))
+ if (!checkDestructuringAssignmentName(target))
return false;
}
}
@@ -4379,32 +4360,28 @@ Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
* these cases, the patterns' property value positions must be
* simple names; the destructuring defines them as new variables.
*
- * In both cases, other code parses the pattern as an arbitrary
- * primaryExpr, and then, here in checkDestructuringPattern, verify
- * that the tree is a valid AssignmentPattern or BindingPattern.
+ * In the first case, other code parses the pattern as an arbitrary
+ * primaryExpr, and then, here in checkDestructuringAssignmentPattern, verify
+ * that the tree is a valid AssignmentPattern.
*
* In assignment-like contexts, we parse the pattern with
- * pc->inDestructuringDecl clear, so the lvalue expressions in the
- * pattern are parsed normally. primaryExpr links variable references
- * into the appropriate use chains; creates placeholder definitions;
- * and so on. checkDestructuringPattern won't bind any new names and
- * we specialize lvalues as appropriate.
+ * pc->inDestructuringDecl clear, so the lvalue expressions in the pattern are
+ * parsed normally. identifierReference() links variable references into the
+ * appropriate use chains; creates placeholder definitions; and so on.
+ * checkDestructuringAssignmentPattern won't bind any new names and we
+ * specialize lvalues as appropriate.
*
- * In declaration-like contexts, the normal variable reference
- * processing would just be an obstruction, because we're going to
- * define the names that appear in the property value positions as new
- * variables anyway. In this case, we parse the pattern with
- * pc->inDestructuringDecl set, which directs primaryExpr to leave
- * whatever name nodes it creates unconnected. Then, here in
- * checkDestructuringPattern, we require the pattern's property value
- * positions to be simple names, and define them as appropriate to the
- * context.
+ * In declaration-like contexts, the normal variable reference processing
+ * would just be an obstruction, because we're going to define the names that
+ * appear in the property value positions as new variables anyway. In this
+ * case, we parse the pattern in destructuringDeclaration() with
+ * pc->inDestructuringDecl set, which directs identifierReference() to leave
+ * whatever name nodes it creates unconnected.
*/
template <>
bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */)
+Parser<FullParseHandler>::checkDestructuringAssignmentPattern(ParseNode* pattern,
+ PossibleError* possibleError /* = nullptr */)
{
if (pattern->isKind(PNK_ARRAYCOMP)) {
errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE);
@@ -4412,8 +4389,8 @@ Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
}
bool isDestructuring = pattern->isKind(PNK_ARRAY)
- ? checkDestructuringArray(pattern, maybeDecl)
- : checkDestructuringObject(pattern, maybeDecl);
+ ? checkDestructuringAssignmentArray(pattern)
+ : checkDestructuringAssignmentObject(pattern);
// Report any pending destructuring error.
if (isDestructuring && possibleError && !possibleError->checkForDestructuringError())
@@ -4422,11 +4399,275 @@ Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
return isDestructuring;
}
+class AutoClearInDestructuringDecl
+{
+ ParseContext* pc_;
+ Maybe<DeclarationKind> saved_;
+
+ public:
+ explicit AutoClearInDestructuringDecl(ParseContext* pc)
+ : pc_(pc),
+ saved_(pc->inDestructuringDecl)
+ {
+ pc->inDestructuringDecl = Nothing();
+ if (saved_ && *saved_ == DeclarationKind::FormalParameter)
+ pc->functionBox()->hasParameterExprs = true;
+ }
+
+ ~AutoClearInDestructuringDecl() {
+ pc_->inDestructuringDecl = saved_;
+ }
+};
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingInitializer(Node lhs, YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+
+ Node rhs;
+ {
+ AutoClearInDestructuringDecl autoClear(pc);
+ rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
+ }
+
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+
+ Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+ if (!assign)
+ return null();
+
+ if (foldConstants && !FoldConstants(context, &assign, this))
+ return null();
+
+ return assign;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling)
+{
+ Rooted<PropertyName*> name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
+
+ Node binding = newName(name);
+ if (!binding || !noteDeclaredName(name, kind, pos()))
+ return null();
+
+ return binding;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+ TokenKind tt)
+{
+ if (tt == TOK_LB)
+ return arrayBindingPattern(kind, yieldHandling);
+
+ if (tt == TOK_LC)
+ return objectBindingPattern(kind, yieldHandling);
+
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_NO_VARIABLE_NAME);
+ return null();
+ }
+
+ return bindingIdentifier(kind, yieldHandling);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+
+ JS_CHECK_RECURSION(context, return null());
+
+ uint32_t begin = pos().begin;
+ Node literal = handler.newObjectLiteral(begin);
+ if (!literal)
+ return null();
+
+ RootedAtom propAtom(context);
+ for (;;) {
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt == TOK_RC)
+ break;
+
+ TokenPos namePos = pos();
+
+ tokenStream.ungetToken();
+
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
+ if (!propName)
+ return null();
+
+ if (propType == PropertyType::Normal) {
+ // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
+
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!binding)
+ return null();
+
+ bool hasInitializer;
+ if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
+ return null();
+
+ Node bindingExpr = hasInitializer
+ ? bindingInitializer(binding, yieldHandling)
+ : binding;
+ if (!bindingExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+ return null();
+ } else if (propType == PropertyType::Shorthand) {
+ // Handle e.g., |var {x, y} = o| as destructuring shorthand
+ // for |var {x: x, y: y} = o|.
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+ Node binding = bindingIdentifier(kind, yieldHandling);
+ if (!binding)
+ return null();
+
+ if (!handler.addShorthand(literal, propName, binding))
+ return null();
+ } else if (propType == PropertyType::CoverInitializedName) {
+ // Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand
+ // with default values.
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+ Node binding = bindingIdentifier(kind, yieldHandling);
+ if (!binding)
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+ Node bindingExpr = bindingInitializer(binding, yieldHandling);
+ if (!bindingExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+ return null();
+ } else {
+ errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
+ return null();
+ }
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt == TOK_RC)
+ break;
+ if (tt != TOK_COMMA) {
+ reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin);
+ return null();
+ }
+ }
+
+ handler.setEndPosition(literal, pos().end);
+ return literal;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+
+ JS_CHECK_RECURSION(context, return null());
+
+ uint32_t begin = pos().begin;
+ Node literal = handler.newArrayLiteral(begin);
+ if (!literal)
+ return null();
+
+ uint32_t index = 0;
+ TokenStream::Modifier modifier = TokenStream::Operand;
+ for (; ; index++) {
+ if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
+ error(JSMSG_ARRAY_INIT_TOO_BIG);
+ return null();
+ }
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (tt == TOK_RB) {
+ tokenStream.ungetToken();
+ break;
+ }
+
+ if (tt == TOK_COMMA) {
+ if (!handler.addElision(literal, pos()))
+ return null();
+ } else if (tt == TOK_TRIPLEDOT) {
+ uint32_t begin = pos().begin;
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!inner)
+ return null();
+
+ if (!handler.addSpreadElement(literal, begin, inner))
+ return null();
+ } else {
+ Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!binding)
+ return null();
+
+ bool hasInitializer;
+ if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
+ return null();
+
+ Node element = hasInitializer ? bindingInitializer(binding, yieldHandling) : binding;
+ if (!element)
+ return null();
+
+ handler.addArrayElement(literal, element);
+ }
+
+ if (tt != TOK_COMMA) {
+ // If we didn't already match TOK_COMMA in above case.
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched) {
+ modifier = TokenStream::None;
+ break;
+ }
+ if (tt == TOK_TRIPLEDOT) {
+ error(JSMSG_REST_WITH_COMMA);
+ return null();
+ }
+ }
+ }
+
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+ reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
+ JSMSG_BRACKET_OPENED, begin));
+
+ handler.setEndPosition(literal, pos().end);
+ return literal;
+}
+
template <>
bool
-Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */)
+Parser<SyntaxParseHandler>::checkDestructuringAssignmentPattern(Node pattern,
+ PossibleError* possibleError /* = nullptr */)
{
return abortIfSyntaxParser();
}
@@ -4439,17 +4680,16 @@ Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandli
MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
- PossibleError possibleError(*this);
Node pattern;
{
pc->inDestructuringDecl = Some(kind);
- pattern = primaryExpr(yieldHandling, TripledotProhibited, tt, &possibleError);
+ if (tt == TOK_LB)
+ pattern = arrayBindingPattern(kind, yieldHandling);
+ else
+ pattern = objectBindingPattern(kind, yieldHandling);
pc->inDestructuringDecl = Nothing();
}
- if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError))
- return null();
-
return pattern;
}
@@ -4564,6 +4804,12 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To
// binary operator (examined with modifier None) terminated |init|.
// For all other declarations, through ASI's infinite majesty, a next
// token on a new line would begin an expression.
+ // Similar to the case in initializerInNameDeclaration(), we need to
+ // peek at the next token when assignExpr() is a lazily parsed arrow
+ // function.
+ TokenKind ignored;
+ if (!tokenStream.peekToken(&ignored))
+ return null();
tokenStream.addModifierException(TokenStream::OperandIsNone);
}
@@ -5975,7 +6221,7 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
// Verify the left-hand side expression doesn't have a forbidden form.
if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) {
- if (!checkDestructuringPattern(*forInitialPart, Nothing(), &possibleError))
+ if (!checkDestructuringAssignmentPattern(*forInitialPart, &possibleError))
return false;
} else if (handler.isNameAnyParentheses(*forInitialPart)) {
const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context);
@@ -7920,26 +8166,6 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli
return handler.newConditional(condition, thenExpr, elseExpr);
}
-class AutoClearInDestructuringDecl
-{
- ParseContext* pc_;
- Maybe<DeclarationKind> saved_;
-
- public:
- explicit AutoClearInDestructuringDecl(ParseContext* pc)
- : pc_(pc),
- saved_(pc->inDestructuringDecl)
- {
- pc->inDestructuringDecl = Nothing();
- if (saved_ && *saved_ == DeclarationKind::FormalParameter)
- pc->functionBox()->hasParameterExprs = true;
- }
-
- ~AutoClearInDestructuringDecl() {
- pc_->inDestructuringDecl = saved_;
- }
-};
-
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
@@ -8170,7 +8396,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return null();
}
- if (!checkDestructuringPattern(lhs, Nothing(), &possibleErrorInner))
+ if (!checkDestructuringAssignmentPattern(lhs, &possibleErrorInner))
return null();
} else if (handler.isNameAnyParentheses(lhs)) {
if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
@@ -9505,7 +9731,7 @@ Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, Node lit
// Turn off the inDestructuringDecl flag when parsing computed property
// names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName()
// should be called on x and y, but not on z. See the comment on
- // Parser<>::checkDestructuringPattern() for details.
+ // Parser<>::checkDestructuringAssignmentPattern() for details.
AutoClearInDestructuringDecl autoClear(pc);
assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (!assignNode)
@@ -9593,8 +9819,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
}
} else if (propType == PropertyType::Shorthand) {
/*
- * Support, e.g., |var {x, y} = o| as destructuring shorthand
- * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer
+ * Support, e.g., |({x, y} = o)| as destructuring shorthand
+ * for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer
* shorthand for |var o = {x: x, y: y}|.
*/
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
@@ -9609,7 +9835,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
return null();
} else if (propType == PropertyType::CoverInitializedName) {
/*
- * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
+ * Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand
* with default values, as per ES6 12.14.5
*/
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index b1d3bdee0..9b6387c9f 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1498,17 +1498,25 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
- // Top-level entrypoint into destructuring pattern checking/name-analyzing.
- bool checkDestructuringPattern(Node pattern, mozilla::Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError = nullptr);
-
- // Recursive methods for checking/name-analyzing subcomponents of a
- // destructuring pattern. The array/object methods *must* be passed arrays
- // or objects. The name method may be passed anything but will report an
- // error if not passed a name.
- bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
- bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
- bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl);
+ Node bindingInitializer(Node lhs, YieldHandling yieldHandling);
+ Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+ Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+ TokenKind tt);
+ Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+ Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+
+ // Top-level entrypoint into destructuring assignment pattern checking and
+ // name-analyzing.
+ bool checkDestructuringAssignmentPattern(Node pattern,
+ PossibleError* possibleError = nullptr);
+
+ // Recursive methods for checking/name-analyzing subcomponents of an
+ // destructuring assignment pattern. The array/object methods *must* be
+ // passed arrays or objects. The name method may be passed anything but
+ // will report an error if not passed a name.
+ bool checkDestructuringAssignmentArray(Node arrayPattern);
+ bool checkDestructuringAssignmentObject(Node objectPattern);
+ bool checkDestructuringAssignmentName(Node expr);
Node newNumber(const Token& tok) {
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);