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 354592 0ad2e4e4cd72b84f178a7b7f1af70760493da1c9
parent 354591 3e2f102ce3b260a6ad809d31de28bd4c0403f1ea
child 354593 d6e29a56658e1931b181eb8c6a7a18d6d1c5b152
push id31707
push userkwierso@gmail.com
push dateMon, 24 Apr 2017 22:53:41 +0000
treeherdermozilla-central@abdcc8dfc283 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1303703
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);