Bug 932080 - Support default values in destructuring; r=jorendorff
authorMorgan Phillips <winter2718@gmail.com>
Sat, 05 Mar 2016 12:51:38 -0800
changeset 286946 bbab21ac3b8cce6a8748fad28c9b4d42e156216d
parent 286945 26fe286d51cd1282d310d0452c177373c9a439eb
child 286947 f4c263f7e968513fdcea038de28a83e1d1f6db89
push id18025
push userryanvm@gmail.com
push dateSun, 06 Mar 2016 20:04:05 +0000
treeherderfx-team@b6acf4d4fc20 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs932080
milestone47.0a1
Bug 932080 - Support default values in destructuring; r=jorendorff
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/basic/destructuring-default.js
js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -721,17 +721,16 @@ Parser<ParseHandler>::checkOptions()
 
     return true;
 }
 
 template <typename ParseHandler>
 Parser<ParseHandler>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
-
     alloc.release(tempPoolMark);
 
     /*
      * The parser can allocate enormous amounts of memory for large functions.
      * Eagerly free the memory now (which otherwise won't be freed until the
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
@@ -3934,16 +3933,80 @@ bool
 Parser<ParseHandler>::AutoPushStmtInfoPC::makeInnermostLexicalScope(StaticBlockScope& blockScope)
 {
     MOZ_ASSERT(parser_.pc->stmtStack.innermost() == &stmt_);
     parser_.pc->stmtStack.makeInnermostLexicalScope(blockScope);
     return generateBlockId();
 }
 
 template <typename ParseHandler>
+Parser<ParseHandler>::PossibleError::PossibleError(Parser<ParseHandler>& parser)
+                                                   : parser_(parser)
+{
+    state_ = ErrorState::None;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPending(ParseReportKind kind, unsigned errorNumber,
+                                                bool strict)
+{
+    // If we report an error later, we'll do it from the position where we set
+    // the state to pending.
+    offset_      = parser_.pos().begin;
+    reportKind_  = kind;
+    strict_      = strict;
+    errorNumber_ = errorNumber;
+    state_       = ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setResolved()
+{
+    state_ = ErrorState::None;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::hasError()
+{
+    return state_ == ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForExprErrors()
+{
+    bool err = hasError();
+    if (err) {
+        parser_.reportWithOffset(reportKind_, strict_, offset_, errorNumber_);
+    }
+    return !err;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::transferErrorTo(PossibleError* other)
+{
+    if (other) {
+        MOZ_ASSERT(this != other);
+        MOZ_ASSERT(!other->hasError());
+        // We should never allow fields to be copied between instances
+        // that point to different underlying parsers.
+        MOZ_ASSERT(&parser_ == &other->parser_);
+        other->offset_        = offset_;
+        other->reportKind_    = reportKind_;
+        other->errorNumber_   = errorNumber_;
+        other->strict_        = strict_;
+        other->state_         = state_;
+    }
+}
+
+template <typename ParseHandler>
 static inline bool
 HasOuterLexicalBinding(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
 {
     while (stmt->enclosingScope) {
         stmt = LexicalLookup(pc, atom, stmt->enclosingScope);
         if (!stmt)
             return false;
         if (stmt->type == StmtType::BLOCK)
@@ -4424,17 +4487,18 @@ Parser<SyntaxParseHandler>::checkDestruc
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                                         TokenKind tt)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     pc->inDeclDestructuring = true;
-    Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt);
+    Node pn = primaryExpr(yieldHandling, TripledotProhibited,
+                          nullptr /* possibleError */, tt);
     pc->inDeclDestructuring = false;
     if (!pn)
         return null();
     if (!checkDestructuringPattern(data, pn))
         return null();
     return pn;
 }
 
@@ -4577,36 +4641,40 @@ Parser<ParseHandler>::newBindingNode(Pro
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
                                                YieldHandling yieldHandling)
 {
     MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF);
-
-    return forHeadKind == PNK_FOROF
+    Node pn = forHeadKind == PNK_FOROF
            ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
            : expr(InAllowed, yieldHandling, TripledotProhibited);
+    return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::declarationPattern(Node decl, TokenKind tt, BindData<ParseHandler>* data,
                                          bool initialDeclaration, YieldHandling yieldHandling,
                                          ParseNodeKind* forHeadKind,
                                          Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
                tokenStream.isCurrentTokenType(TOK_LC));
 
     Node pattern;
     {
         pc->inDeclDestructuring = true;
-        pattern = primaryExpr(yieldHandling, TripledotProhibited, tt);
+
+        // No possible error is required because we already know we're
+        // destructuring.
+        pattern = primaryExpr(yieldHandling, TripledotProhibited,
+                              nullptr /* possibleError */ , tt);
         pc->inDeclDestructuring = false;
     }
     if (!pattern)
         return null();
 
     if (initialDeclaration && forHeadKind) {
         bool isForIn, isForOf;
         if (!matchInOrOf(&isForIn, &isForOf))
@@ -4643,17 +4711,24 @@ Parser<ParseHandler>::declarationPattern
     // See comment below for bindBeforeInitializer in the code that
     // handles the non-destructuring case.
     bool bindBeforeInitializer = handler.declarationIsVar(decl);
     if (bindBeforeInitializer) {
         if (!checkDestructuringPattern(data, pattern))
             return null();
     }
 
-    MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
+    TokenKind token;
+    if (!tokenStream.getToken(&token, TokenStream::None))
+        return null();
+
+    if (token != TOK_ASSIGN) {
+        report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL);
+        return null();
+    }
 
     Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
                            yieldHandling, TripledotProhibited);
     if (!init)
         return null();
 
     if (forHeadKind) {
         // For for(;;) declarations, consistency with |for (;| parsing requires
@@ -6819,17 +6894,16 @@ Parser<ParseHandler>::tryStatement(Yield
             Node catchName;
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
                 catchName = destructuringExpr(yieldHandling, &data, tt);
                 if (!catchName)
                     return null();
                 break;
-
               case TOK_YIELD:
                 if (yieldHandling == YieldIsKeyword) {
                     report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
                     return null();
                 }
 
                 // Even if yield is *not* necessarily a keyword, we still must
                 // check its validity for legacy generators.
@@ -7454,46 +7528,79 @@ Parser<ParseHandler>::statement(YieldHan
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                           TripledotHandling tripledotHandling, InvokedPrediction invoked)
-{
-    Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked);
+                           TripledotHandling tripledotHandling,
+                           PossibleError* possibleError,
+                           InvokedPrediction invoked)
+{
+    Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+                         possibleError, invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
 
-        pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
+        // Additional calls to assignExpr should not reuse the possibleError
+        // which had been passed into the function. Otherwise we would lose
+        // information needed to determine whether or not we're dealing with
+        // a non-recoverable situation.
+        PossibleError possibleErrorInner(*this);
+        pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+                        &possibleErrorInner);
         if (!pn)
             return null();
+
+        // If we find an error here we should report it immedately instead of
+        // passing it back out of the function.
+        if (possibleErrorInner.hasError())  {
+
+            // We begin by checking for an outer pending error since it would
+            // have occurred first.
+            if (possibleError->checkForExprErrors())
+                possibleErrorInner.checkForExprErrors();
+            return null();
+        }
         handler.addList(seq, pn);
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
     }
     return seq;
 }
 
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
+                           TripledotHandling tripledotHandling,
+                           InvokedPrediction invoked)
+{
+    PossibleError possibleError(*this);
+    Node pn = expr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked);
+    if (!pn || !possibleError.checkForExprErrors())
+        return null();
+    return pn;
+}
+
 static const JSOp ParseNodeKindToJSOp[] = {
     JSOP_OR,
     JSOP_AND,
     JSOP_BITOR,
     JSOP_BITXOR,
     JSOP_BITAND,
     JSOP_STRICTEQ,
     JSOP_EQ,
@@ -7571,30 +7678,32 @@ Precedence(ParseNodeKind pnk) {
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                              TripledotHandling tripledotHandling, InvokedPrediction invoked)
+                              TripledotHandling tripledotHandling,
+                              PossibleError* possibleError,
+                              InvokedPrediction invoked)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     Node pn;
     for (;;) {
-        pn = unaryExpr(yieldHandling, tripledotHandling, invoked);
+        pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
@@ -7634,54 +7743,64 @@ Parser<ParseHandler>::orExpr1(InHandling
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                                TripledotHandling tripledotHandling, InvokedPrediction invoked)
-{
-    Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
+                                TripledotHandling tripledotHandling,
+                                PossibleError* possibleError,
+                                InvokedPrediction invoked)
+{
+    Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
-
-    Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+    Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+                               possibleError);
     if (!thenExpr)
         return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
-    Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+    Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited,
+                               possibleError);
     if (!elseExpr)
         return null();
 
     // 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);
 }
 
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor)
+Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor,
+                                                  PossibleError* possibleError)
 {
     MOZ_ASSERT(flavor != KeyedDestructuringAssignment,
                "destructuring must use special checking/marking code, not "
                "this method");
 
     if (handler.isUnparenthesizedDestructuringPattern(target)) {
         if (flavor == CompoundAssignment) {
             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
 
-        return checkDestructuringPattern(nullptr, target);
+        bool isDestructuring = checkDestructuringPattern(nullptr, target);
+        // Here we've successfully distinguished between destructuring and
+        // an object literal. In the case where "CoverInitializedName"
+        // syntax was used there will be a pending error that needs clearing.
+        if (possibleError && isDestructuring)
+            possibleError->setResolved();
+        return isDestructuring;
     }
 
     // All other permitted targets are simple.
     if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
         return false;
 
     if (handler.isPropertyAccess(target))
         return true;
@@ -7699,19 +7818,22 @@ Parser<ParseHandler>::checkAndMarkAsAssi
 
     MOZ_ASSERT(handler.isFunctionCall(target));
     return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                 TripledotHandling tripledotHandling, InvokedPrediction invoked)
+                                 TripledotHandling tripledotHandling,
+                                 PossibleError* possibleError,
+                                 InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
+    MOZ_ASSERT(!possibleError->hasError());
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
     // numeric arrays, such as some Kraken benchmarks, it happens more often.)
     //
@@ -7751,19 +7873,21 @@ Parser<ParseHandler>::assignExpr(InHandl
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
-    Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
-    if (!lhs)
-        return null();
+    PossibleError possibleErrorInner(*this);
+    Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
+    if (!lhs) {
+        return null();
+    }
 
     ParseNodeKind kind;
     JSOp op;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
       case TOK_SUBASSIGN:    kind = PNK_SUBASSIGN;    op = JSOP_SUB;    break;
       case TOK_BITORASSIGN:  kind = PNK_BITORASSIGN;  op = JSOP_BITOR;  break;
@@ -7773,16 +7897,17 @@ Parser<ParseHandler>::assignExpr(InHandl
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    op = JSOP_RSH;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   op = JSOP_URSH;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
+
         // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
         tokenStream.ungetToken();
         TokenKind next = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "expression", TokenKindToDesc(TOK_ARROW));
             return null();
         }
@@ -7835,35 +7960,53 @@ Parser<ParseHandler>::assignExpr(InHandl
                 return null();
             tokenStream.addModifierException(TokenStream::NoneIsOperand);
         }
         return arrowFunc;
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
+        possibleErrorInner.transferErrorTo(possibleError);
         tokenStream.ungetToken();
         return lhs;
     }
 
     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
-    if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
+    if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner))
+        return null();
+    if (!possibleErrorInner.checkForExprErrors())
         return null();
 
     bool saved = pc->inDeclDestructuring;
     pc->inDeclDestructuring = false;
-    Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+    Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited,
+                          possibleError);
     pc->inDeclDestructuring = saved;
     if (!rhs)
         return null();
-
     return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
 template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                 TripledotHandling tripledotHandling,
+                                 InvokedPrediction invoked)
+{
+    PossibleError possibleError(*this);
+    Node expr = assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked);
+    if (!expr || !possibleError.checkForExprErrors())
+        return null();
+
+    return expr;
+}
+
+
+template <typename ParseHandler>
 bool
 Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node,
                                                     FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
 {
     // Note that this method implements *only* a boolean test.  Reporting an
     // error for the various syntaxes that fail this, and warning for the
     // various syntaxes that "pass" this but should not, occurs elsewhere.
 
@@ -7977,26 +8120,27 @@ Parser<ParseHandler>::checkAndMarkAsIncO
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                   uint32_t begin)
 {
-    Node kid = unaryExpr(yieldHandling, TripledotProhibited);
-    if (!kid)
+    PossibleError possibleError(*this);
+    Node kid = unaryExpr(yieldHandling, TripledotProhibited, &possibleError);
+    if (!kid || !possibleError.checkForExprErrors())
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                InvokedPrediction invoked)
+                                PossibleError* possibleError, InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
@@ -8018,59 +8162,59 @@ Parser<ParseHandler>::unaryExpr(YieldHan
         //
         //   // Looks up the name, doesn't find it and so evaluates to
         //   // "undefined".
         //   assertEq(typeof nonExistentName, "undefined");
         //
         //   // Evaluates expression, triggering a runtime ReferenceError for
         //   // the undefined name.
         //   typeof (1, nonExistentName);
-        Node kid = unaryExpr(yieldHandling, TripledotProhibited);
+        Node kid = unaryExpr(yieldHandling, TripledotProhibited, possibleError);
         if (!kid)
             return null();
 
         return handler.newTypeof(begin, kid);
       }
 
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
-        Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true);
+        Node pn2 = memberExpr(yieldHandling, TripledotProhibited, possibleError, tt2, true);
         if (!pn2)
             return null();
         AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
         if (!checkAndMarkAsIncOperand(pn2, flavor))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
                                 pn2);
       }
 
       case TOK_DELETE: {
-        Node expr = unaryExpr(yieldHandling, TripledotProhibited);
+        Node expr = unaryExpr(yieldHandling, TripledotProhibited, possibleError);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
         if (handler.isNameAnyParentheses(expr)) {
             if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       default: {
-        Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+        Node pn = memberExpr(yieldHandling, tripledotHandling, possibleError, tt, /* allowCallSyntax = */ true,
                              invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
@@ -8481,17 +8625,18 @@ Parser<ParseHandler>::checkAndMarkSuperS
         return false;
     pc->sc->markSuperScopeNeedsHomeObject();
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                 TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
+                                 PossibleError* possibleError, TokenKind tt,
+                                 bool allowCallSyntax, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
@@ -8505,17 +8650,18 @@ Parser<ParseHandler>::memberExpr(YieldHa
             lhs = newTarget;
         } else {
             lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
             if (!lhs)
                 return null();
 
             // Gotten by tryNewTarget
             tt = tokenStream.currentToken().type;
-            Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked);
+            Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited,
+                                       possibleError, tt, false, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
             handler.addList(lhs, ctorExpr);
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_LP))
                 return null();
@@ -8530,17 +8676,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
     } else if (tt == TOK_SUPER) {
         Node thisName = newThisName();
         if (!thisName)
             return null();
         lhs = handler.newSuperBase(thisName, pos());
         if (!lhs)
             return null();
     } else {
-        lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked);
+        lhs = primaryExpr(yieldHandling, tripledotHandling, possibleError, tt, invoked);
         if (!lhs)
             return null();
     }
 
     MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
@@ -8561,17 +8707,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TOK_LB) {
-            Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+            Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
             if (!propExpr)
                 return null();
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
 
             if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
                     return null();
@@ -8686,16 +8832,29 @@ Parser<ParseHandler>::memberExpr(YieldHa
         return null();
     }
 
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
+Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+                                 bool allowCallSyntax, InvokedPrediction invoked)
+{
+    PossibleError possibleError(*this);
+    Node pn = memberExpr(yieldHandling, tripledotHandling, &possibleError, tt,
+                         allowCallSyntax, invoked);
+    if (!pn || !possibleError.checkForExprErrors())
+        return null();
+    return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName* name)
 {
     return handler.newName(name, pc->blockid(), pos(), context);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
@@ -8996,23 +9155,25 @@ Parser<ParseHandler>::propertyName(Yield
         if (isGenerator) {
             report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
             return null();
         }
         *propType = PropertyType::Normal;
         return propName;
     }
 
-    if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
+    if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) {
         if (isGenerator) {
             report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
             return null();
         }
         tokenStream.ungetToken();
-        *propType = PropertyType::Shorthand;
+        *propType = tt == TOK_ASSIGN ?
+                          PropertyType::CoverInitializedName :
+                          PropertyType::Shorthand;
         return propName;
     }
 
     if (tt == TOK_LP) {
         tokenStream.ungetToken();
         *propType = isGenerator ? PropertyType::GeneratorMethod : PropertyType::Method;
         return propName;
     }
@@ -9043,25 +9204,26 @@ Parser<ParseHandler>::computedPropertyNa
     if (!propname)
         return null();
     handler.setListFlag(literal, PNX_NONCONST);
     return propname;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling)
+Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
+    bool seenCoverInitializedName = false;
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
         if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
             return null();
         if (tt == TOK_RC)
             break;
 
@@ -9110,16 +9272,53 @@ Parser<ParseHandler>::objectLiteral(Yiel
                 return null();
 
             Node nameExpr = identifierName(yieldHandling);
             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
+             * with default values, as per ES6 12.14.5 (2016/2/4)
+             */
+            if (!tokenStream.checkForKeyword(propAtom, nullptr))
+                return null();
+
+            Node lhs = identifierName(yieldHandling);
+            if (!lhs)
+                return null();
+
+            tokenStream.consumeKnownToken(TOK_ASSIGN);
+            Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+            if (!rhs)
+                return null();
+
+            Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP);
+            if (!propExpr)
+                return null();
+
+            if (!handler.addPropertyDefinition(literal, propName, propExpr))
+                return null();
+
+            if (!abortIfSyntaxParser())
+                return null();
+
+            if (!seenCoverInitializedName) {
+                seenCoverInitializedName = true;
+                // "shorthand default" or "CoverInitializedName" syntax is only
+                // valid in the case of destructuring. 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.
+                if (possibleError)
+                    possibleError->setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+            }
+
         } else {
             // FIXME: Implement ES6 function "name" property semantics
             // (bug 883377).
             RootedPropertyName funName(context);
             switch (propType) {
               case PropertyType::Getter:
               case PropertyType::Setter:
                 funName = nullptr;
@@ -9209,33 +9408,34 @@ Parser<ParseHandler>::tryNewTarget(Node 
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                  TokenKind tt, InvokedPrediction invoked)
+                                  PossibleError* possibleError, TokenKind tt,
+                                  InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(invoked);
 
       case TOK_CLASS:
         return classDefinition(yieldHandling, ClassExpression, NameRequired);
 
       case TOK_LB:
         return arrayInitializer(yieldHandling);
 
       case TOK_LC:
-        return objectLiteral(yieldHandling);
+        return objectLiteral(yieldHandling, possibleError);
 
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
         if (next == TOK_RP) {
             // Not valid expression syntax, but this is valid in an arrow function
@@ -9257,17 +9457,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
         }
 
         if (next == TOK_FOR) {
             uint32_t begin = pos().begin;
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
             return generatorComprehension(begin);
         }
 
-        Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed);
+        Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
         if (!expr)
             return null();
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
         handler.setEndPosition(expr, pos().end);
         return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
@@ -9364,16 +9564,26 @@ Parser<ParseHandler>::primaryExpr(YieldH
                "expression", TokenKindToDesc(tt));
         return null();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                                   TripledotHandling tripledotHandling,
+                                   PossibleError* possibleError)
+{
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+    return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                                    TripledotHandling tripledotHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, PredictInvoked);
 }
 
 template <typename ParseHandler>
 void
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -369,16 +369,17 @@ struct BindData;
 
 class CompExprTransplanter;
 
 enum VarContext { HoistVars, DontHoistVars };
 enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
 enum class PropertyType {
     Normal,
     Shorthand,
+    CoverInitializedName,
     Getter,
     GetterNoExpressionClosure,
     Setter,
     SetterNoExpressionClosure,
     Method,
     GeneratorMethod,
     Constructor,
     DerivedConstructor
@@ -410,16 +411,82 @@ class Parser : private JS::AutoGCRooter,
         bool generateBlockId();
         bool makeInnermostLexicalScope(StaticBlockScope& blockScope);
 
         StmtInfoPC& operator*() { return stmt_; }
         StmtInfoPC* operator->() { return &stmt_; }
         operator StmtInfoPC*() { return &stmt_; }
     };
 
+    /*
+     * A class for temporarily stashing errors while parsing continues.
+     *
+     * The ability to stash an error is useful for handling situations where we
+     * aren't able to verify that an error has occurred until later in the parse.
+     * For instance | ({x=1}) | is always parsed as an object literal with
+     * a SyntaxError, however, in the case where it is followed by '=>' we rewind
+     * and reparse it as a valid arrow function. Here a PossibleError would be
+     * set to 'pending' when the initial SyntaxError was encountered then 'resolved'
+     * just before rewinding the parser.
+     *
+     * When using PossibleError one should set a pending error at the location
+     * where an error occurs. From that point, the error may be resolved
+     * (invalidated) or left until the PossibleError is checked.
+     *
+     * Ex:
+     *   PossibleError possibleError(*this);
+     *   possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+     *   // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
+     *   possibleError.checkForExprErrors();
+     *
+     *   PossibleError possibleError(*this);
+     *   possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+     *   possibleError.setResolved();
+     *   // Returns true, no error is reported.
+     *   possibleError.checkForExprErrors();
+     *
+     *   PossibleError possibleError(*this);
+     *   // Returns true, no error is reported.
+     *   possibleError.checkForExprErrors();
+     */
+    class MOZ_STACK_CLASS PossibleError
+    {
+        enum ErrorState { None, Pending };
+        ErrorState state_;
+
+        // Error reporting fields.
+        uint32_t offset_;
+        unsigned errorNumber_;
+        ParseReportKind reportKind_;
+        Parser<ParseHandler>& parser_;
+        bool strict_;
+
+        public:
+          explicit PossibleError(Parser<ParseHandler>& parser);
+
+          // Set a pending error.
+          void setPending(ParseReportKind kind, unsigned errorNumber, bool strict);
+
+          // Resolve any pending error.
+          void setResolved();
+
+          // Return true if an error is pending without reporting
+          bool hasError();
+
+          // If there is a pending error report it and return false, otherwise return
+          // true.
+          bool checkForExprErrors();
+
+          // 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 transferErrorTo(PossibleError* other);
+    };
+
   public:
     ExclusiveContext* const context;
     LifoAlloc& alloc;
 
     TokenStream         tokenStream;
     LifoAlloc::Mark     tempPoolMark;
 
     /* list of parsed objects for GC tracing */
@@ -760,35 +827,53 @@ class Parser : private JS::AutoGCRooter,
     // |forHeadKind|.
     bool initializerInNameDeclaration(Node decl, Node binding, Handle<PropertyName*> name,
                                       BindData<ParseHandler>* data, bool initialDeclaration,
                                       YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
                                       Node* forInOrOfExpression);
 
     Node expr(InHandling inHandling, YieldHandling yieldHandling,
               TripledotHandling tripledotHandling,
+              PossibleError* possibleError,
               InvokedPrediction invoked = PredictUninvoked);
+    Node expr(InHandling inHandling, YieldHandling yieldHandling,
+              TripledotHandling tripledotHandling,
+              InvokedPrediction invoked = PredictUninvoked);
+    Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                    TripledotHandling tripledotHandling,
+                    PossibleError* possibleError,
+                    InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                     TripledotHandling tripledotHandling,
                     InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
     Node yieldExpression(InHandling inHandling);
     Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
                    TripledotHandling tripledotHandling,
+                   PossibleError* possibleError,
                    InvokedPrediction invoked = PredictUninvoked);
     Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
                  TripledotHandling tripledotHandling,
-                   InvokedPrediction invoked = PredictUninvoked);
+                 PossibleError* possibleError,
+                 InvokedPrediction invoked = PredictUninvoked);
     Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                   PossibleError* possibleError,
                    InvokedPrediction invoked = PredictUninvoked);
+    Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                    PossibleError* possibleError, TokenKind tt,
+                    bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
     Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                     bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
-    Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+    Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                     PossibleError* possibleError, TokenKind tt,
                      InvokedPrediction invoked = PredictUninvoked);
     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                      TripledotHandling tripledotHandling,
+                      PossibleError* possibleError);
+    Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                       TripledotHandling tripledotHandling);
 
     bool tryNewTarget(Node& newTarget);
     bool checkAndMarkSuperScope();
 
     Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
                           HandlePropertyName funName);
 
@@ -849,17 +934,18 @@ class Parser : private JS::AutoGCRooter,
         PlainAssignment,
         CompoundAssignment,
         KeyedDestructuringAssignment,
         IncrementAssignment,
         DecrementAssignment,
         ForInOrOfTarget
     };
 
-    bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
+    bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
+                                     PossibleError* possibleError=nullptr);
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool checkFunctionArguments();
 
     bool defineFunctionThis();
     Node newThisName();
 
     bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
@@ -904,17 +990,17 @@ class Parser : private JS::AutoGCRooter,
     Node pushLetScope(Handle<StaticBlockScope*> blockScope, AutoPushStmtInfoPC& stmt);
     bool noteNameUse(HandlePropertyName name, Node pn);
     Node propertyName(YieldHandling yieldHandling, Node propList,
                       PropertyType* propType, MutableHandleAtom propAtom);
     Node computedPropertyName(YieldHandling yieldHandling, Node literal);
     Node arrayInitializer(YieldHandling yieldHandling);
     Node newRegExp();
 
-    Node objectLiteral(YieldHandling yieldHandling);
+    Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
 
     enum PrepareLexicalKind {
         PrepareLet,
         PrepareConst,
         PrepareFunction
     };
     bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
     bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
--- a/js/src/jit-test/tests/basic/destructuring-default.js
+++ b/js/src/jit-test/tests/basic/destructuring-default.js
@@ -1,27 +1,33 @@
 
 load(libdir + 'asserts.js');
 load(libdir + 'eqArrayHelper.js');
 
 var arrayPattern = '[a = 1, b = 2, c = 3, d = 4, e = 5, f = 6]';
 var objectPattern = '{0: a = 1, 1: b = 2, 2: c = 3, 3: d = 4, 4: e = 5, 5: f = 6}';
+var objectPatternShorthand = '{a = 1, b = 2, c = 3, d = 4, e = 5, f = 6}';
 var nestedPattern = '{a: a = 1, b: [b = 2] = [], c: {c: [c]} = {c: [3]}, d: {d, e} = {d: 4, e: 5}, f: f = 6}';
 
 function testAll(fn) {
   assertEqArray(fn(arrayPattern, []), [1, 2, 3, 4, 5, 6]);
   assertEqArray(fn(arrayPattern, [2, 3, 4, 5, 6, 7, 8, 9]), [2, 3, 4, 5, 6, 7]);
   assertEqArray(fn(arrayPattern, [undefined, 0, false, null, "", undefined]), [1, 0, false, null, "", 6]);
   assertEqArray(fn(arrayPattern, [0, false]), [0, false, 3, 4, 5, 6]);
 
   assertEqArray(fn(objectPattern, []), [1, 2, 3, 4, 5, 6]);
   assertEqArray(fn(objectPattern, [2, 3, 4, 5, 6, 7, 8, 9]), [2, 3, 4, 5, 6, 7]);
   assertEqArray(fn(objectPattern, [undefined, 0, false, null, "", undefined]), [1, 0, false, null, "", 6]);
   assertEqArray(fn(objectPattern, [0, false]), [0, false, 3, 4, 5, 6]);
 
+  assertEqArray(fn(objectPatternShorthand, {}), [1, 2, 3, 4, 5, 6]);
+  assertEqArray(fn(objectPatternShorthand, {a: 2, b: 3, c: 4, d: 5, e: 6, f: 7, g: 8, h: 9}), [2, 3, 4, 5, 6, 7]);
+  assertEqArray(fn(objectPatternShorthand, {a: undefined, b: 0, c: false, d: null, e: "", f: undefined}),
+                   [1, 0, false, null, "", 6]);
+  assertEqArray(fn(objectPatternShorthand, {a: 0, b: false}), [0, false, 3, 4, 5, 6]);
   assertEqArray(fn(nestedPattern, {}), [1, 2, 3, 4, 5, 6]);
   assertEqArray(fn(nestedPattern, {a: 2, b: [], c: undefined}), [2, 2, 3, 4, 5, 6]);
   assertEqArray(fn(nestedPattern, {a: undefined, b: [3], c: {c: [4]}}), [1, 3, 4, 4, 5, 6]);
   assertEqArray(fn(nestedPattern, {a: undefined, b: [3], c: {c: [4]}, d: {d: 5, e: 6}}), [1, 3, 4, 5, 6, 6]);
 }
 
 function testVar(pattern, input) {
   return new Function('input',
@@ -170,10 +176,14 @@ if (defaultsSupportedInForVar) {
 
     b = undefined;
     for (var {1: b = 10} in " ") {}
     assertEq(b, 10);
 
     b = undefined;
     for (let {1: c = 10} in " ") { b = c; }
     assertEq(b, 10);
+
+    b = undefined;
+    for (let {c = 10} in " ") { b = c; }
+    assertEq(b, 10);
   `)();
 }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js
@@ -0,0 +1,102 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Ensure that the syntax used in shorthand destructuring with defaults
+// e.g. |{x=1, y=2} = {}| properly raises a syntax error within an object
+// literal. As per ES6 12.2.6 Object Initializer, "NOTE 3."
+
+const SYNTAX_ERROR_STMTS = [
+    // expressions
+    "({x={}={}}),",
+    "({y={x={}={}={}={}={}={}={}={}}={}}),",
+    "({a=1, b=2, c=3, x=({}={})}),",
+    "({x=1, y={z={1}}})",
+    "({x=1} = {y=1});",
+    "({x: y={z=1}}={})",
+    "({x=1}),",
+    "({z=1}={}, {w=2}, {e=3})=>{};",
+    "({z={x=1}})=>{};",
+    "({x = ({y=1}) => y})",
+    "(({x=1})) => x",
+    // declarations
+    "let o = {x=1};",
+    "var j = {x=1};",
+    "var j = {x={y=1}}={};",
+    "const z = {x=1};",
+    "const z = {x={y=1}}={};",
+    "const {x=1};",
+    "const {x={y=33}}={};",
+    "var {x=1};",
+    "let {x=1};",
+    "let x, y, {z=1}={}, {w=2}, {e=3};",
+    // array initialization
+    "[{x=1, y = ({z=2} = {})}];",
+    // try/catch
+    "try {throw 'a';} catch ({x={y=1}}) {}",
+    // if/else
+    "if ({k: 1, x={y=2}={}}) {}",
+    "if (false) {} else if (true) { ({x=1}) }",
+    // switch
+    "switch ('c') { case 'c': ({x=1}); }",
+    // for
+    "for ({x=1}; 1;) {1}",
+    "for ({x={y=2}}; 1;) {1}",
+    "for (var x = 0; x < 2; x++) { ({x=1, y=2}) }",
+    "for (let x=1;{x=1};){}",
+    "for (let x=1;{x={y=2}};){}",
+    "for (let x=1;1;{x=1}){}",
+    "for (let x=1;1;{x={y=2}}){}",
+    // while
+    "while ({x=1}) {1};",
+    "while ({x={y=2}}={}) {1};",
+    // with
+    "with ({x=1}) {};",
+    "with ({x={y=3}={}}) {};",
+    "with (Math) { ({x=1}) };"
+]
+
+for (var stmt of SYNTAX_ERROR_STMTS) {
+    assertThrowsInstanceOf(() => {
+        eval(stmt);
+    }, SyntaxError);
+}
+
+const REFERENCE_ERROR_STMTS = [
+    "({x} += {});",
+    "({x = 1}) = {x: 2};",
+]
+
+for (var stmt of REFERENCE_ERROR_STMTS) {
+    assertThrowsInstanceOf(() => {
+        eval(stmt);
+    }, ReferenceError);
+}
+
+// A few tricky but acceptable cases:
+// see https://bugzilla.mozilla.org/show_bug.cgi?id=932080#c2
+
+assertEq((({a = 0}) => a)({}), 0);
+assertEq((({a = 0} = {}) => a)({}), 0);
+assertEq((({a = 0} = {}) => a)({a: 1}), 1);
+
+{
+    let x, y;
+    ({x=1} = {});
+    assertEq(x, 1);
+    ({x=1} = {x: 4});
+    assertEq(x, 4);
+    ({x=1, y=2} = {})
+    assertEq(x, 1);
+    assertEq(y, 2);
+}
+
+{
+    let {x={i=1, j=2}={}}={};
+    assertDeepEq(x, ({}));
+    assertEq(i, 1);
+    assertEq(j, 2);
+}
+
+if (typeof reportCompare == "function")
+    reportCompare(true, true);