Bug 1303703 - Part 1: Separate binding pattern parsing from object/array literal parsing. r=shu
authorAndré Bargull <andre.bargull@gmail.com>
Sat, 22 Apr 2017 02:04:27 -0700
changeset 567063 0ad2e4e4cd72b84f178a7b7f1af70760493da1c9
parent 567062 3e2f102ce3b260a6ad809d31de28bd4c0403f1ea
child 567064 d6e29a56658e1931b181eb8c6a7a18d6d1c5b152
push id55429
push userbmo:hskupin@gmail.com
push dateMon, 24 Apr 2017 10:48:57 +0000
reviewersshu
bugs1303703
milestone55.0a1
Bug 1303703 - Part 1: Separate binding pattern parsing from object/array literal parsing. r=shu
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4281,47 +4281,30 @@ Parser<ParseHandler, CharT>::PossibleErr
                "Can't transfer fields to an instance which belongs to a different parser");
 
     transferErrorTo(ErrorKind::Destructuring, other);
     transferErrorTo(ErrorKind::Expression, other);
 }
 
 template <>
 bool
-Parser<FullParseHandler, char16_t>::checkDestructuringName(ParseNode* expr,
-                                                           const Maybe<DeclarationKind>& maybeDecl)
+Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentName(ParseNode* expr)
 {
     MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
 
     // Parentheses are forbidden around destructuring *patterns* (but allowed
     // around names).  Use our nicer error message for parenthesized, nested
     // patterns.
     if (handler.isParenthesizedDestructuringPattern(expr)) {
         errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_PARENS);
         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))
                 return false;
         }
 
         return true;
     }
@@ -4330,33 +4313,30 @@ Parser<FullParseHandler, char16_t>::chec
         return true;
 
     errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET);
     return false;
 }
 
 template <>
 bool
-Parser<FullParseHandler, char16_t>::checkDestructuringPattern(ParseNode* pattern,
-                                                              const Maybe<DeclarationKind>& maybeDecl,
-                                                              PossibleError* possibleError /* = nullptr */);
+Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentPattern(ParseNode* pattern,
+                                                                        PossibleError* possibleError /* = nullptr */);
 
 template <>
 bool
-Parser<SyntaxParseHandler, char16_t>::checkDestructuringPattern(Node pattern,
-                                                                const Maybe<DeclarationKind>& maybeDecl,
-                                                                PossibleError* possibleError /* = nullptr */)
+Parser<SyntaxParseHandler, char16_t>::checkDestructuringAssignmentPattern(Node pattern,
+                                                                          PossibleError* possibleError /* = nullptr */)
 {
     return abortIfSyntaxParser();
 }
 
 template <>
 bool
-Parser<FullParseHandler, char16_t>::checkDestructuringObject(ParseNode* objectPattern,
-                                                             const Maybe<DeclarationKind>& maybeDecl)
+Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentObject(ParseNode* objectPattern)
 {
     MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
 
     for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) {
         ParseNode* target;
         if (member->isKind(PNK_MUTATEPROTO)) {
             target = member->pn_kid;
         } else {
@@ -4367,31 +4347,30 @@ Parser<FullParseHandler, char16_t>::chec
                           member->pn_left->pn_atom == member->pn_right->pn_atom);
 
             target = member->pn_right;
         }
         if (handler.isUnparenthesizedAssignment(target))
             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;
         }
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler, char16_t>::checkDestructuringArray(ParseNode* arrayPattern,
-                                                            const Maybe<DeclarationKind>& maybeDecl)
+Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentArray(ParseNode* arrayPattern)
 {
     MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
 
     for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
         if (element->isKind(PNK_ELISION))
             continue;
 
         ParseNode* target;
@@ -4403,20 +4382,20 @@ Parser<FullParseHandler, char16_t>::chec
             target = element->pn_kid;
         } else if (handler.isUnparenthesizedAssignment(element)) {
             target = element->pn_left;
         } else {
             target = element;
         }
 
         if (handler.isUnparenthesizedDestructuringPattern(target)) {
-            if (!this->checkDestructuringPattern(target, maybeDecl))
+            if (!this->checkDestructuringAssignmentPattern(target))
                 return false;
         } else {
-            if (!checkDestructuringName(target, maybeDecl))
+            if (!checkDestructuringAssignmentName(target))
                 return false;
         }
     }
 
     return true;
 }
 
 /*
@@ -4427,79 +4406,342 @@ Parser<FullParseHandler, char16_t>::chec
  *   arbitrary lvalue expressions; the destructuring is just a fancy
  *   assignment.
  *
  * - binding-like: |var| and |let| declarations, functions' formal
  *   parameter lists, |catch| clauses, and comprehension tails.  In
  *   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, char16_t>::checkDestructuringPattern(ParseNode* pattern,
-                                                              const Maybe<DeclarationKind>& maybeDecl,
-                                                              PossibleError* possibleError /* = nullptr */)
+Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentPattern(ParseNode* pattern,
+                                                                        PossibleError* possibleError /* = nullptr */)
 {
     if (pattern->isKind(PNK_ARRAYCOMP)) {
         errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
     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())
         return false;
 
     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 <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
+                                                  YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+
+    if (!CheckRecursionLimit(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 <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+
+    if (!CheckRecursionLimit(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 <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
                                                       YieldHandling yieldHandling,
                                                       TokenKind tt)
 {
     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;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
                                                                          YieldHandling yieldHandling,
                                                                          TokenKind tt)
@@ -4606,16 +4848,22 @@ Parser<ParseHandler, CharT>::declaration
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
     if (forHeadKind) {
         // For for(;;) declarations, consistency with |for (;| parsing requires
         // that the ';' first be examined as Operand, even though absence of a
         // 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);
     }
 
     return handler.newBinary(PNK_ASSIGN, pattern, init);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 bool
@@ -6036,17 +6284,17 @@ Parser<ParseHandler, CharT>::forHeadStar
         errorAt(exprOffset, JSMSG_LET_STARTING_FOROF_LHS);
         return false;
     }
 
     *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF;
 
     // 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);
         if (chars) {
             // |chars| is "arguments" or "eval" here.
             if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
@@ -7983,36 +8231,16 @@ Parser<ParseHandler, CharT>::condExpr1(I
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     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 <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                                         TripledotHandling tripledotHandling,
                                         PossibleError* possibleError /* = nullptr */,
                                         InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
@@ -8234,17 +8462,17 @@ Parser<ParseHandler, CharT>::assignExpr(
 
     // Verify the left-hand side expression doesn't have a forbidden form.
     if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
         if (kind != PNK_ASSIGN) {
             error(JSMSG_BAD_DESTRUCT_ASS);
             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)) {
             // |chars| is "arguments" or "eval" here.
             if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return null();
         }
 
@@ -9584,17 +9812,17 @@ Parser<ParseHandler, CharT>::computedPro
 {
     uint32_t begin = pos().begin;
 
     Node assignNode;
     {
         // 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)
             return null();
     }
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
     Node propname = handler.newComputedName(assignNode, begin, pos().end);
@@ -9673,33 +9901,33 @@ Parser<ParseHandler, CharT>::objectLiter
                 if (!handler.isConstant(propExpr))
                     handler.setListFlag(literal, PNX_NONCONST);
 
                 if (!handler.addPropertyDefinition(literal, propName, propExpr))
                     return null();
             }
         } 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));
             if (!name)
                 return null();
 
             Node nameExpr = identifierReference(name);
             if (!nameExpr)
                 return null();
 
             if (!handler.addShorthand(literal, propName, nameExpr))
                 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));
             if (!name)
                 return null();
 
             Node lhs = identifierReference(name);
             if (!lhs)
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1521,27 +1521,35 @@ class Parser final : public ParserBase, 
     Node propertyName(YieldHandling yieldHandling, Node propList,
                       PropertyType* propType, MutableHandleAtom propAtom);
     Node computedPropertyName(YieldHandling yieldHandling, Node literal);
     Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
     Node newRegExp();
 
     Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
 
-    // Top-level entrypoint into destructuring pattern checking/name-analyzing.
-    bool checkDestructuringPattern(Node pattern, const mozilla::Maybe<DeclarationKind>& maybeDecl,
-                                   PossibleError* possibleError = nullptr);
+    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);
 
-    // 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, const mozilla::Maybe<DeclarationKind>& maybeDecl);
-    bool checkDestructuringObject(Node objectPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
-    bool checkDestructuringName(Node expr, const mozilla::Maybe<DeclarationKind>& maybeDecl);
+    // 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);
     }
 
     static Node null() { return ParseHandler<CharT>::null(); }
 
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);