Bug 1303703 - Part 3: Syntax parse destructuring assignment patterns. r=shu
authorAndré Bargull <andre.bargull@gmail.com>
Sat, 22 Apr 2017 02:19:10 -0700
changeset 402711 8f2c0ac318b10cac245519338ed8556bcfce09e6
parent 402710 d6e29a56658e1931b181eb8c6a7a18d6d1c5b152
child 402712 332fa2188d891a1aa15c9c98fd2e1f7007a46239
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [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 3: Syntax parse destructuring assignment patterns. r=shu
js/src/frontend/FullParseHandler.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/js.msg
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -860,27 +860,31 @@ class FullParseHandlerBase
     bool isUnparenthesizedName(ParseNode* node) {
         return node->isKind(PNK_NAME) && !node->isInParens();
     }
 
     bool isNameAnyParentheses(ParseNode* node) {
         return node->isKind(PNK_NAME);
     }
 
+    bool isArgumentsAnyParentheses(ParseNode* node, JSContext* cx) {
+        return node->isKind(PNK_NAME) && node->pn_atom == cx->names().arguments;
+    }
+
     bool isEvalAnyParentheses(ParseNode* node, JSContext* cx) {
         return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval;
     }
 
     const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, JSContext* cx) {
         MOZ_ASSERT(isNameAnyParentheses(node),
                    "must only call this function on known names");
 
         if (isEvalAnyParentheses(node, cx))
             return js_eval_str;
-        if (node->pn_atom == cx->names().arguments)
+        if (isArgumentsAnyParentheses(node, cx))
             return js_arguments_str;
         return nullptr;
     }
 
     bool isAsyncKeyword(ParseNode* node, JSContext* cx) {
         return node->isKind(PNK_NAME) &&
                node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
                node->pn_atom == cx->names().async;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -695,16 +695,29 @@ ParserBase::extraWarning(unsigned errorN
     bool result =
         tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
 bool
+ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
+{
+    va_list args;
+    va_start(args, errorNumber);
+
+    bool result =
+        tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args);
+
+    va_end(args);
+    return result;
+}
+
+bool
 ParserBase::strictModeError(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool res =
         tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
                                                   errorNumber, args);
@@ -4166,18 +4179,20 @@ Parser<ParseHandler, CharT>::PossibleErr
 {}
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename Parser<ParseHandler, CharT>::PossibleError::Error&
 Parser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
 {
     if (kind == ErrorKind::Expression)
         return exprError_;
-    MOZ_ASSERT(kind == ErrorKind::Destructuring);
-    return destructuringError_;
+    if (kind == ErrorKind::Destructuring)
+        return destructuringError_;
+    MOZ_ASSERT(kind == ErrorKind::DestructuringWarning);
+    return destructuringWarning_;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
 {
     error(kind).state_ = ErrorState::None;
 }
@@ -4185,16 +4200,23 @@ Parser<ParseHandler, CharT>::PossibleErr
 template <template <typename CharT> class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
 {
     return error(kind).state_ == ErrorState::Pending;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
+bool
+Parser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
+{
+    return hasError(ErrorKind::Destructuring);
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
                                                        unsigned errorNumber)
 {
     // Don't overwrite a previously recorded error.
     if (hasError(kind))
         return;
 
@@ -4211,16 +4233,24 @@ void
 Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
                                                                            unsigned errorNumber)
 {
     setPending(ErrorKind::Destructuring, pos, errorNumber);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 void
+Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
+                                                                             unsigned errorNumber)
+{
+    setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
+void
 Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
                                                                         unsigned errorNumber)
 {
     setPending(ErrorKind::Expression, pos, errorNumber);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 bool
@@ -4231,33 +4261,46 @@ Parser<ParseHandler, CharT>::PossibleErr
 
     Error& err = error(kind);
     parser_.errorAt(err.offset_, err.errorNumber_);
     return false;
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringError()
+Parser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
+{
+    if (!hasError(kind))
+        return true;
+
+    Error& err = error(kind);
+    return parser_.extraWarningAt(err.offset_, err.errorNumber_);
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
+bool
+Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning()
 {
     // Clear pending expression error, because we're definitely not in an
     // expression context.
     setResolved(ErrorKind::Expression);
 
-    // Report any pending destructuring error.
-    return checkForError(ErrorKind::Destructuring);
+    // Report any pending destructuring error or warning.
+    return checkForError(ErrorKind::Destructuring) &&
+           checkForWarning(ErrorKind::DestructuringWarning);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
 {
-    // Clear pending destructuring error, because we're definitely not in a
-    // destructuring context.
+    // Clear pending destructuring error or warning, because we're definitely
+    // not in a destructuring context.
     setResolved(ErrorKind::Destructuring);
+    setResolved(ErrorKind::DestructuringWarning);
 
     // Report any pending expression error.
     return checkForError(ErrorKind::Expression);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind, PossibleError* other)
@@ -4279,168 +4322,16 @@ Parser<ParseHandler, CharT>::PossibleErr
     MOZ_ASSERT(this != other);
     MOZ_ASSERT(&parser_ == &other->parser_,
                "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>::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;
-    }
-
-    // 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;
-    }
-
-    if (handler.isPropertyAccess(expr))
-        return true;
-
-    errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET);
-    return false;
-}
-
-template <>
-bool
-Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentPattern(ParseNode* pattern,
-                                                                        PossibleError* possibleError /* = nullptr */);
-
-template <>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkDestructuringAssignmentPattern(Node pattern,
-                                                                          PossibleError* possibleError /* = nullptr */)
-{
-    return abortIfSyntaxParser();
-}
-
-template <>
-bool
-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 {
-            MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
-            MOZ_ASSERT_IF(member->isKind(PNK_SHORTHAND),
-                          member->pn_left->isKind(PNK_OBJECT_PROPERTY_NAME) &&
-                          member->pn_right->isKind(PNK_NAME) &&
-                          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 (!checkDestructuringAssignmentPattern(target))
-                return false;
-        } else {
-            if (!checkDestructuringAssignmentName(target))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-template <>
-bool
-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;
-        if (element->isKind(PNK_SPREAD)) {
-            if (element->pn_next) {
-                errorAt(element->pn_next->pn_pos.begin, JSMSG_PARAMETER_AFTER_REST);
-                return false;
-            }
-            target = element->pn_kid;
-        } else if (handler.isUnparenthesizedAssignment(element)) {
-            target = element->pn_left;
-        } else {
-            target = element;
-        }
-
-        if (handler.isUnparenthesizedDestructuringPattern(target)) {
-            if (!this->checkDestructuringAssignmentPattern(target))
-                return false;
-        } else {
-            if (!checkDestructuringAssignmentName(target))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-/*
- * Destructuring patterns can appear in two kinds of contexts:
- *
- * - assignment-like: assignment expressions and |for| loop heads.  In
- *   these cases, the patterns' property value positions can be
- *   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 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.
- */
-template <>
-bool
-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)
-                           ? checkDestructuringAssignmentArray(pattern)
-                           : checkDestructuringAssignmentObject(pattern);
-
-    // Report any pending destructuring error.
-    if (isDestructuring && possibleError && !possibleError->checkForDestructuringError())
-        return false;
-
-    return isDestructuring;
-}
-
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
                                                 YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
@@ -6245,17 +6136,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 (!checkDestructuringAssignmentPattern(*forInitialPart, &possibleError))
+        if (!possibleError.checkForDestructuringErrorOrWarning())
             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;
         }
@@ -8214,17 +8105,17 @@ Parser<ParseHandler, CharT>::assignExpr(
     // In such cases, we can avoid the full expression parsing route through
     // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
     // primaryExpr().
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
-    uint32_t exprOffset = pos().begin;
+    TokenPos exprPos = pos();
 
     bool endsExpr;
 
     // This only handles identifiers that *never* have special meaning anywhere
     // in the language.  Contextual keywords, reserved words in strict mode,
     // and other hard cases are handled outside this fast path.
     if (tt == TOK_NAME) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
@@ -8420,33 +8311,36 @@ 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 (!checkDestructuringAssignmentPattern(lhs, &possibleErrorInner))
+        if (!possibleErrorInner.checkForDestructuringErrorOrWarning())
             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))
+            if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return null();
         }
 
         handler.adjustGetToSet(lhs);
     } else if (handler.isPropertyAccess(lhs)) {
         // Permitted: no additional testing/fixup needed.
     } else if (handler.isFunctionCall(lhs)) {
-        if (!strictModeErrorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS))
-            return null();
+        if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS))
+            return null();
+
+        if (possibleError)
+            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
     } else {
-        errorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS);
+        errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS);
         return null();
     }
 
     if (!possibleErrorInner.checkForExpressionError())
         return null();
 
     Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     if (!rhs)
@@ -9463,16 +9357,84 @@ Parser<ParseHandler, CharT>::newRegExp()
     reobj = RegExpObject::create(context, chars, length, flags, &tokenStream, alloc);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
 template <template <typename CharT> class ParseHandler, typename CharT>
+void
+Parser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                                                PossibleError* possibleError)
+{
+    // Return early if a pending destructuring error is already present.
+    if (possibleError->hasPendingDestructuringError())
+        return;
+
+    if (pc->sc()->needStrictChecks()) {
+        if (handler.isArgumentsAnyParentheses(expr, context)) {
+            if (pc->sc()->strict()) {
+                possibleError->setPendingDestructuringErrorAt(exprPos,
+                                                              JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
+            } else {
+                possibleError->setPendingDestructuringWarningAt(exprPos,
+                                                                JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
+            }
+            return;
+        }
+
+        if (handler.isEvalAnyParentheses(expr, context)) {
+            if (pc->sc()->strict()) {
+                possibleError->setPendingDestructuringErrorAt(exprPos,
+                                                              JSMSG_BAD_STRICT_ASSIGN_EVAL);
+            } else {
+                possibleError->setPendingDestructuringWarningAt(exprPos,
+                                                                JSMSG_BAD_STRICT_ASSIGN_EVAL);
+            }
+            return;
+        }
+    }
+
+    // The expression must be either a simple assignment target, i.e. a name
+    // or a property accessor, or a nested destructuring pattern.
+    if (!handler.isUnparenthesizedDestructuringPattern(expr) &&
+        !handler.isNameAnyParentheses(expr) &&
+        !handler.isPropertyAccess(expr))
+    {
+        // Parentheses are forbidden around destructuring *patterns* (but
+        // allowed around names). Use our nicer error message for
+        // parenthesized, nested patterns.
+        if (handler.isParenthesizedDestructuringPattern(expr))
+            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS);
+        else
+            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+    }
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
+void
+Parser<ParseHandler, CharT>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                                                 PossibleError* possibleError)
+{
+    // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834
+    // 12.15.5 Destructuring Assignment
+    //
+    // AssignmentElement[Yield, Await]:
+    //   DestructuringAssignmentTarget[?Yield, ?Await]
+    //   DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]
+
+    // If |expr| is an assignment element with an initializer expression, its
+    // destructuring assignment target was already validated in assignExpr().
+    // Otherwise we need to check that |expr| is a valid destructuring target.
+    if (!handler.isUnparenthesizedAssignment(expr))
+        checkDestructuringAssignmentTarget(expr, exprPos, possibleError);
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::Node
 Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
                                               PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
@@ -9512,27 +9474,40 @@ Parser<ParseHandler, CharT>::arrayInitia
 
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
             } else if (tt == TOK_TRIPLEDOT) {
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
+
+                TokenPos innerPos;
+                if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
+                    return null();
+
                 Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
                                         possibleError);
                 if (!inner)
                     return null();
+                if (possibleError)
+                    checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
                 if (!handler.addSpreadElement(literal, begin, inner))
                     return null();
             } else {
+                TokenPos elementPos;
+                if (!tokenStream.peekTokenPos(&elementPos, TokenStream::Operand))
+                    return null();
+
                 Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
                                           possibleError);
                 if (!element)
                     return null();
+                if (possibleError)
+                    checkDestructuringAssignmentElement(element, elementPos, possibleError);
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
             if (tt != TOK_COMMA) {
                 /* If we didn't already match TOK_COMMA in above case. */
                 bool matched;
@@ -9813,16 +9788,20 @@ Parser<ParseHandler, CharT>::objectLiter
         tokenStream.ungetToken();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
         if (!propName)
             return null();
 
         if (propType == PropertyType::Normal) {
+            TokenPos exprPos;
+            if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
+                return null();
+
             Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
                                        possibleError);
             if (!propExpr)
                 return null();
 
             handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
 
             if (foldConstants && !FoldConstants(context, &propExpr, this))
@@ -9852,30 +9831,36 @@ Parser<ParseHandler, CharT>::objectLiter
                     return null();
             } else {
                 if (!handler.isConstant(propExpr))
                     handler.setListFlag(literal, PNX_NONCONST);
 
                 if (!handler.addPropertyDefinition(literal, propName, propExpr))
                     return null();
             }
+
+            if (possibleError)
+                checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
         } else if (propType == PropertyType::Shorthand) {
             /*
              * 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 (possibleError)
+                checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
+
             if (!handler.addShorthand(literal, propName, nameExpr))
                 return null();
         } else if (propType == PropertyType::CoverInitializedName) {
             /*
              * 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));
@@ -9902,31 +9887,34 @@ Parser<ParseHandler, CharT>::objectLiter
                 }
 
                 // Here we set a pending error so that later in the parse, once we've
                 // determined whether or not we're destructuring, the error can be
                 // reported or ignored appropriately.
                 possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
             }
 
+            if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
+                // |chars| is "arguments" or "eval" here.
+                if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
+                    return null();
+            }
+
             Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
             if (!rhs)
                 return null();
 
             handler.checkAndSetIsDirectRHSAnonFunction(rhs);
 
             Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
             if (!propExpr)
                 return null();
 
             if (!handler.addPropertyDefinition(literal, propName, propExpr))
                 return null();
-
-            if (!abortIfSyntaxParser())
-                return null();
         } else {
             RootedAtom funName(context);
             if (!tokenStream.isCurrentTokenType(TOK_RB)) {
                 funName = propAtom;
 
                 if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
                     funName = prefixAccessorName(propType, propAtom);
                     if (!funName)
@@ -9938,16 +9926,19 @@ Parser<ParseHandler, CharT>::objectLiter
             if (!fn)
                 return null();
 
             handler.checkAndSetIsDirectRHSAnonFunction(fn);
 
             JSOp op = JSOpFromPropertyType(propType);
             if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
                 return null();
+
+            if (possibleError)
+                possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET);
         }
 
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
         if (tt != TOK_COMMA) {
             reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -886,16 +886,22 @@ class ParserBase : public StrictModeGett
     MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
 
     /*
      * If extra warnings are enabled, report the given warning at the current
      * offset.
      */
     MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
 
+    /*
+     * If extra warnings are enabled, report the given warning at the given
+     * offset.
+     */
+    MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
+
     bool isValidStrictBinding(PropertyName* name);
 
     void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
     bool warnOnceAboutForEach();
 
     bool allowsForEachIn() {
@@ -1015,72 +1021,87 @@ class Parser final : public ParserBase, 
      *   PossibleError possibleError(*this);
      *   // Returns true, no error is reported.
      *   if (!possibleError.checkForExpressionError())
      *       return false; // not reached, no pending exception
      */
     class MOZ_STACK_CLASS PossibleError
     {
       private:
-        enum class ErrorKind { Expression, Destructuring };
+        enum class ErrorKind { Expression, Destructuring, DestructuringWarning };
 
         enum class ErrorState { None, Pending };
 
         struct Error {
             ErrorState state_ = ErrorState::None;
 
             // Error reporting fields.
             uint32_t offset_;
             unsigned errorNumber_;
         };
 
         Parser<ParseHandler, CharT>& parser_;
         Error exprError_;
         Error destructuringError_;
+        Error destructuringWarning_;
 
         // Returns the error report.
         Error& error(ErrorKind kind);
 
-        // Return true if an error is pending without reporting
+        // Return true if an error is pending without reporting.
         bool hasError(ErrorKind kind);
 
         // Resolve any pending error.
         void setResolved(ErrorKind kind);
 
         // Set a pending error. Only a single error may be set per instance and
         // error kind.
         void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber);
 
         // If there is a pending error, report it and return false, otherwise
         // return true.
-        bool checkForError(ErrorKind kind);
+        MOZ_MUST_USE bool checkForError(ErrorKind kind);
+
+        // If there is a pending warning, report it and return either false or
+        // true depending on the werror option, otherwise return true.
+        MOZ_MUST_USE bool checkForWarning(ErrorKind kind);
 
         // Transfer an existing error to another instance.
         void transferErrorTo(ErrorKind kind, PossibleError* other);
 
       public:
         explicit PossibleError(Parser<ParseHandler, CharT>& parser);
 
+        // Return true if a pending destructuring error is present.
+        bool hasPendingDestructuringError();
+
         // Set a pending destructuring error. Only a single error may be set
         // per instance, i.e. subsequent calls to this method are ignored and
         // won't overwrite the existing pending error.
         void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
 
+        // Set a pending destructuring warning. Only a single warning may be
+        // set per instance, i.e. subsequent calls to this method are ignored
+        // and won't overwrite the existing pending warning.
+        void setPendingDestructuringWarningAt(const TokenPos& pos, unsigned errorNumber);
+
         // Set a pending expression error. Only a single error may be set per
         // instance, i.e. subsequent calls to this method are ignored and won't
         // overwrite the existing pending error.
         void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber);
 
-        // If there is a pending destructuring error, report it and return
-        // false, otherwise return true. Clears any pending expression error.
-        bool checkForDestructuringError();
+        // If there is a pending destructuring error or warning, report it and
+        // return false, otherwise return true. Clears any pending expression
+        // error.
+        MOZ_MUST_USE bool checkForDestructuringErrorOrWarning();
 
         // If there is a pending expression error, report it and return false,
-        // otherwise return true. Clears any pending destructuring error.
-        bool checkForExpressionError();
+        // otherwise return true. Clears any pending destructuring error or
+        // warning.
+        MOZ_MUST_USE bool checkForExpressionError();
 
         // Pass pending errors between possible error instances. This is useful
         // for extending the lifetime of a pending error beyond the scope of
         // the PossibleError where it was initially set (keeping in mind that
         // PossibleError is a MOZ_STACK_CLASS).
         void transferErrorsTo(PossibleError* other);
     };
 
@@ -1519,28 +1540,20 @@ class Parser final : public ParserBase, 
 
     Node bindingInitializer(Node lhs, DeclarationKind kind, 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);
+    void checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                            PossibleError* possibleError);
+    void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                             PossibleError* possibleError);
 
     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);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -571,27 +571,31 @@ class SyntaxParseHandlerBase
     bool isNameAnyParentheses(Node node) {
         if (isUnparenthesizedName(node))
             return true;
         return node == NodeParenthesizedArgumentsName ||
                node == NodeParenthesizedEvalName ||
                node == NodeParenthesizedName;
     }
 
+    bool isArgumentsAnyParentheses(Node node, JSContext* cx) {
+        return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName;
+    }
+
     bool isEvalAnyParentheses(Node node, JSContext* cx) {
         return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName;
     }
 
     const char* nameIsArgumentsEvalAnyParentheses(Node node, JSContext* cx) {
         MOZ_ASSERT(isNameAnyParentheses(node),
                    "must only call this method on known names");
 
         if (isEvalAnyParentheses(node, cx))
             return js_eval_str;
-        if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName)
+        if (isArgumentsAnyParentheses(node, cx))
             return js_arguments_str;
         return nullptr;
     }
 
     bool isAsyncKeyword(Node node, JSContext* cx) {
         return node == NodePotentialAsyncKeyword;
     }
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -208,16 +208,18 @@ MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    0
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,         1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,       0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_BAD_METHOD_DEF,          0, JSEXN_SYNTAXERR, "bad method definition")
 MSG_DEF(JSMSG_BAD_OCTAL,               1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
 MSG_DEF(JSMSG_BAD_POW_LEFTSIDE,        0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'")
 MSG_DEF(JSMSG_BAD_PROP_ID,             0, JSEXN_SYNTAXERR, "invalid property id")
 MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1, JSEXN_SYNTAXERR, "{0} not in function")
 MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS, 0, JSEXN_SYNTAXERR, "'arguments' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_EVAL,  0, JSEXN_SYNTAXERR, "'eval' can't be defined or assigned to in strict mode code")
 MSG_DEF(JSMSG_BAD_SWITCH,              0, JSEXN_SYNTAXERR, "invalid switch statement")
 MSG_DEF(JSMSG_BAD_SUPER,               0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
 MSG_DEF(JSMSG_BAD_SUPERPROP,           1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
 MSG_DEF(JSMSG_BAD_SUPERCALL,           0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
 MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
 MSG_DEF(JSMSG_BRACKET_AFTER_LIST,      0, JSEXN_SYNTAXERR, "missing ] after element list")
 MSG_DEF(JSMSG_BRACKET_IN_INDEX,        0, JSEXN_SYNTAXERR, "missing ] in index expression")
 MSG_DEF(JSMSG_BRACKET_OPENED,          2, JSEXN_NOTE, "[ opened at line {0}, column {1}")