Bug 933276 - Implement [...x] in assignment target (spread operator); r=jorendorff
authorArpad Borsos <arpad.borsos@googlemail.com>
Fri, 22 Aug 2014 10:16:58 +0200
changeset 201322 5c2363e6e9ad7d5b9ae67bc1790c36a23bfd6701
parent 201321 3b2046d81e3590a13740f511e595128f86e62413
child 201323 ff1dd4dd6984defc6f071d455b90b78f75a27387
push id27366
push userryanvm@gmail.com
push dateMon, 25 Aug 2014 15:49:45 +0000
treeherdermozilla-central@4ca2bd0722d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs933276
milestone34.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 933276 - Implement [...x] in assignment target (spread operator); r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/arguments/destructuring-exprbody.js
js/src/jit-test/tests/auto-regress/bug532363.js
js/src/jit-test/tests/auto-regress/bug596817.js
js/src/jit-test/tests/auto-regress/bug785776.js
js/src/jit-test/tests/baseline/bug843429.js
js/src/jit-test/tests/baseline/bug847678.js
js/src/jit-test/tests/baseline/setcall.js
js/src/jit-test/tests/basic/bug685321-1.js
js/src/jit-test/tests/basic/bug685321-2.js
js/src/jit-test/tests/basic/bug778268.js
js/src/jit-test/tests/basic/bug862228.js
js/src/jit-test/tests/basic/destructuring-iterator.js
js/src/jit-test/tests/basic/destructuring-rest-identifiers.js
js/src/jit-test/tests/basic/destructuring-rest.js
js/src/jit-test/tests/basic/expression-autopsy.js
js/src/jit-test/tests/basic/spread-call-setcall.js
js/src/jit-test/tests/basic/testBug714650.js
js/src/jit-test/tests/basic/testLet.js
js/src/jit-test/tests/basic/testNonStubGetter.js
js/src/jit-test/tests/closures/bug540348.js
js/src/jit-test/tests/ion/bug743099.js
js/src/jit-test/tests/ion/bug821794.js
js/src/jit-test/tests/jaeger/bug588363-1.js
js/src/jit-test/tests/parser/bug-889628.js
js/src/js.msg
js/src/jsreflect.cpp
js/src/tests/js1_5/extensions/regress-469625.js
js/src/tests/js1_7/extensions/regress-346642-06.js
js/src/tests/js1_7/extensions/regress-351102-03.js
js/src/tests/js1_7/extensions/regress-368224.js
js/src/tests/js1_8/regress/regress-469625-03.js
js/src/tests/js1_8_1/regress/regress-452498-052.js
js/src/tests/js1_8_5/extensions/reflect-parse.js
js/src/tests/js1_8_5/regress/regress-609617.js
js/src/tests/js1_8_5/regress/regress-646820-3.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3021,19 +3021,24 @@ EmitDestructuringDecl(ExclusiveContext *
 static bool
 EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
                        ParseNode *pattern)
 {
     if (pattern->isKind(PNK_ARRAY)) {
         for (ParseNode *element = pattern->pn_head; element; element = element->pn_next) {
             if (element->isKind(PNK_ELISION))
                 continue;
+            ParseNode *target = element;
+            if (element->isKind(PNK_SPREAD)) {
+                JS_ASSERT(element->pn_kid->isKind(PNK_NAME));
+                target = element->pn_kid;
+            }
             DestructuringDeclEmitter emitter =
-                element->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
-            if (!emitter(cx, bce, prologOp, element))
+                target->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
+            if (!emitter(cx, bce, prologOp, target))
                 return false;
         }
         return true;
     }
 
     MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
     for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
         ParseNode *target = member->pn_right;
@@ -3065,16 +3070,18 @@ static bool
 EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
 {
     JS_ASSERT(emitOption != DefineVars);
 
     // Now emit the lvalue opcode sequence. If the lvalue is a nested
     // destructuring initialiser-form, call ourselves to handle it, then pop
     // the matched value. Otherwise emit an lvalue bytecode sequence followed
     // by an assignment op.
+    if (pn->isKind(PNK_SPREAD))
+        pn = pn->pn_kid;
     if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
         if (!EmitDestructuringOpsHelper(cx, bce, pn, emitOption))
             return false;
         if (emitOption == InitializeVars) {
             // Per its post-condition, EmitDestructuringOpsHelper has left the
             // to-be-destructured value on top of the stack.
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
@@ -3182,16 +3189,38 @@ EmitDestructuringLHS(ExclusiveContext *c
         // Pop the assigned value.
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
 
     return true;
 }
 
+static bool EmitSpread(ExclusiveContext *cx, BytecodeEmitter *bce);
+static bool EmitIterator(ExclusiveContext *cx, BytecodeEmitter *bce);
+
+/**
+ * EmitIteratorNext will pop iterator from the top of the stack.
+ * It will push the result of |.next()| onto the stack.
+ */
+static bool
+EmitIteratorNext(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn=nullptr)
+{
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... ITER ITER
+        return false;
+    if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ... ITER NEXT
+        return false;
+    if (Emit1(cx, bce, JSOP_SWAP) < 0)                         // ... NEXT ITER
+        return false;
+    if (EmitCall(cx, bce, JSOP_CALL, 0, pn) < 0)               // ... RESULT
+        return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
+    return true;
+}
+
 /*
  * Recursive helper for EmitDestructuringOps.
  * EmitDestructuringOpsHelper assumes the to-be-destructured value has been
  * pushed on the stack and emits code to destructure each part of a [] or {}
  * lhs expression.
  *
  * If emitOption is InitializeVars, the initial to-be-destructured value is
  * left untouched on the stack and the overall depth is not changed.
@@ -3201,47 +3230,54 @@ EmitDestructuringLHS(ExclusiveContext *c
  * lhs expression. (Same post-condition as EmitDestructuringLHS)
  */
 static bool
 EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
                            VarEmitOption emitOption)
 {
     JS_ASSERT(emitOption != DefineVars);
 
-    unsigned index;
     ParseNode *pn2, *pn3;
     bool doElemOp;
+    bool needToPopIterator = false;
 
 #ifdef DEBUG
     int stackDepth = bce->stackDepth;
     JS_ASSERT(stackDepth != 0);
     JS_ASSERT(pn->isArity(PN_LIST));
     JS_ASSERT(pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT));
 #endif
 
-    index = 0;
+    /*
+     * When destructuring an array, use an iterator to walk it, instead of index lookup.
+     * InitializeVars expects us to leave the *original* value on the stack.
+     */
+    if (pn->isKind(PNK_ARRAY)) {
+        if (emitOption == InitializeVars) {
+            if (Emit1(cx, bce, JSOP_DUP) < 0)                      // OBJ OBJ
+                return false;
+        }
+        if (!EmitIterator(cx, bce))                                // OBJ? ITER
+            return false;
+        needToPopIterator = true;
+    }
+
     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
-        /* Duplicate the value being destructured to use as a reference base. */
-        if (Emit1(cx, bce, JSOP_DUP) < 0)
-            return false;
-
         /*
-         * Now push the property name currently being matched, which is either
-         * the array initialiser's current index, or the current property name
-         * "label" on the left of a colon in the object initialiser.  Set pn3
-         * to the lvalue node, which is in the value-initializing position.
+         * Now push the property name currently being matched, which is the
+         * current property name "label" on the left of a colon in the object initialiser.
+         * Set pn3 to the lvalue node, which is in the value-initializing position.
          */
-        doElemOp = true;
-        if (pn->isKind(PNK_ARRAY)) {
-            if (!EmitNumberOp(cx, index, bce))
+        if (pn->isKind(PNK_OBJECT)) {
+            doElemOp = true;
+            JS_ASSERT(pn2->isKind(PNK_COLON) || pn2->isKind(PNK_SHORTHAND));
+
+            /* Duplicate the value being destructured to use as a reference base. */
+            if (Emit1(cx, bce, JSOP_DUP) < 0)
                 return false;
-            pn3 = pn2;
-        } else {
-            JS_ASSERT(pn->isKind(PNK_OBJECT));
-            JS_ASSERT(pn2->isKind(PNK_COLON) || pn2->isKind(PNK_SHORTHAND));
 
             ParseNode *key = pn2->pn_left;
             if (key->isKind(PNK_NUMBER)) {
                 if (!EmitNumberOp(cx, key->pn_dval, bce))
                     return false;
             } else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
                 PropertyName *name = key->pn_atom->asPropertyName();
 
@@ -3258,43 +3294,104 @@ EmitDestructuringOpsHelper(ExclusiveCont
                     doElemOp = false;
                 }
             } else {
                 JS_ASSERT(key->isKind(PNK_COMPUTED_NAME));
                 if (!EmitTree(cx, bce, key->pn_kid))
                     return false;
             }
 
+            if (doElemOp) {
+                /*
+                 * Ok, get the value of the matching property name.  This leaves
+                 * that value on top of the value being destructured, so the stack
+                 * is one deeper than when we started.
+                 */
+                if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
+                    return false;
+                JS_ASSERT(bce->stackDepth >= stackDepth + 1);
+            }
+
             pn3 = pn2->pn_right;
-        }
-
-
-        if (doElemOp) {
-            /*
-             * Ok, get the value of the matching property name.  This leaves
-             * that value on top of the value being destructured, so the stack
-             * is one deeper than when we started.
-             */
-            if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
-                return false;
-            JS_ASSERT(bce->stackDepth >= stackDepth + 1);
+        } else {
+            JS_ASSERT(pn->isKind(PNK_ARRAY));
+
+            if (pn2->isKind(PNK_SPREAD)) {
+                /* Create a new array with the rest of the iterator */
+                ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);          // ITER ARRAY
+                if (off < 0)
+                    return false;
+                CheckTypeSet(cx, bce, JSOP_NEWARRAY);
+                jsbytecode *pc = bce->code(off);
+                SET_UINT24(pc, 0);
+
+                if (!EmitNumberOp(cx, 0, bce))                             // ITER ARRAY INDEX
+                    return false;
+                if (!EmitSpread(cx, bce))                                  // ARRAY INDEX
+                    return false;
+                if (Emit1(cx, bce, JSOP_POP) < 0)                          // ARRAY
+                    return false;
+                if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
+                    return false;
+                needToPopIterator = false;
+            } else {
+                if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER ITER
+                    return false;
+                if (!EmitIteratorNext(cx, bce, pn))                        // ITER RESULT
+                    return false;
+                if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER RESULT RESULT
+                    return false;
+                if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce))  // ITER RESULT DONE?
+                    return false;
+
+                // Emit (result.done ? undefined : result.value)
+                // This is mostly copied from EmitConditionalExpression, except that this code
+                // does not push new values onto the stack.
+                ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
+                if (noteIndex < 0)
+                    return false;
+                ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
+                if (beq < 0)
+                    return false;
+
+                if (Emit1(cx, bce, JSOP_POP) < 0)                          // ITER
+                    return false;
+                if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                    // ITER UNDEFINED
+                    return false;
+
+                /* Jump around else, fixup the branch, emit else, fixup jump. */
+                ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
+                if (jmp < 0)
+                    return false;
+                SetJumpOffsetAt(bce, beq);
+
+                if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // ITER VALUE
+                    return false;
+
+                SetJumpOffsetAt(bce, jmp);
+                if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
+                    return false;
+            }
+
+            pn3 = pn2;
         }
 
         /* Elision node makes a hole in the array destructurer. */
         if (pn3->isKind(PNK_ELISION)) {
             JS_ASSERT(pn->isKind(PNK_ARRAY));
             JS_ASSERT(pn2 == pn3);
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
         } else {
             int32_t depthBefore = bce->stackDepth;
             if (!EmitDestructuringLHS(cx, bce, pn3, emitOption))
                 return false;
 
-            if (emitOption == PushInitialValues) {
+            if (emitOption == PushInitialValues &&
+                (pn->isKind(PNK_OBJECT) || needToPopIterator)) {
                 /*
                  * After '[x,y]' in 'let ([[x,y], z] = o)', the stack is
                  *   | to-be-destructured-value | x | y |
                  * The goal is:
                  *   | x | y | z |
                  * so emit a pick to produce the intermediate state
                  *   | x | y | to-be-destructured-value |
                  * before destructuring z. This gives the loop invariant that
@@ -3307,24 +3404,26 @@ EmitDestructuringOpsHelper(ExclusiveCont
                         bce->reportError(pn3, JSMSG_TOO_MANY_LOCALS);
                         return false;
                     }
                     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0)
                         return false;
                 }
             }
         }
-
-        ++index;
-    }
-
-    if (emitOption == PushInitialValues) {
+    }
+
+    if (needToPopIterator && Emit1(cx, bce, JSOP_POP) < 0)
+        return false;
+
+    if (emitOption == PushInitialValues && pn->isKind(PNK_OBJECT)) {
         /*
          * Per the above loop invariant, to-be-destructured-value is at the top
          * of the stack. To achieve the post-condition, pop it.
+         * In case of array destructuring, the above POP already took care of the iterator.
          */
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
 
     return true;
 }
 
@@ -4413,23 +4512,16 @@ EmitForOf(ExclusiveContext *cx, Bytecode
         if (!EmitTree(cx, bce, forHead->pn_kid3))
             return false;
         if (!EmitIterator(cx, bce))
             return false;
 
         // Push a dummy result so that we properly enter iteration midstream.
         if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                // ITER RESULT
             return false;
-    } else {
-        // If type is STMT_SPREAD, it expects that iterator to be already on
-        // the stack.
-        if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)      // I ITER ARR
-            return false;
-        if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)      // ITER ARR I
-            return false;
     }
 
     // Enter the block before the loop body, after evaluating the obj.
     StmtInfoBCE letStmt(cx);
     if (letDecl) {
         if (!EnterBlockScope(cx, bce, &letStmt, pn1->pn_objbox, 0))
             return false;
     }
@@ -4502,25 +4594,18 @@ EmitForOf(ExclusiveContext *cx, Bytecode
         if (Emit1(cx, bce, JSOP_POP) < 0)                      // ITER
             return false;
         if (Emit1(cx, bce, JSOP_DUP) < 0)                      // ITER ITER
             return false;
     } else {
         if (!EmitDupAt(cx, bce, bce->stackDepth - 1 - 2))      // ITER ARR I ITER
             return false;
     }
-    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... ITER ITER
-        return false;
-    if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ... ITER NEXT
-        return false;
-    if (Emit1(cx, bce, JSOP_SWAP) < 0)                         // ... NEXT ITER
-        return false;
-    if (EmitCall(cx, bce, JSOP_CALL, 0, forHead) < 0)          // ... RESULT
-        return false;
-    CheckTypeSet(cx, bce, JSOP_CALL);
+    if (!EmitIteratorNext(cx, bce, forHead))                   // ... RESULT
+        return false;
     if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... RESULT RESULT
         return false;
     if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce))  // ... RESULT DONE?
         return false;
 
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset()); // ... RESULT
     if (beq < 0)
         return false;
@@ -6052,21 +6137,22 @@ EmitArrayComp(ExclusiveContext *cx, Byte
         return false;
     bce->arrayCompDepth = saveDepth;
 
     /* Emit the usual op needed for decompilation. */
     return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
 }
 
 /**
- * EmitSpread expects the iterator, the current index (I) of the array, and the
- * array itself to be on the stack in that order (iterator on the top). It will
- * pop the iterator and I, then iterate over the iterator by calling |.next()|
+ * EmitSpread expects the current index (I) of the array, the array itself and the iterator to be
+ * on the stack in that order (iterator on the bottom).
+ * It will pop the iterator and I, then iterate over the iterator by calling |.next()|
  * and put the results into the I-th element of array with incrementing I, then
  * push the result I (it will be original I + iteration count).
+ * The stack after iteration will look like |ARRAY INDEX|.
  */
 static bool
 EmitSpread(ExclusiveContext *cx, BytecodeEmitter *bce)
 {
     return EmitForOf(cx, bce, STMT_SPREAD, nullptr, -1);
 }
 
 static bool
@@ -6082,63 +6168,67 @@ EmitArray(ExclusiveContext *cx, Bytecode
      */
 
     int32_t nspread = 0;
     for (ParseNode *elt = pn; elt; elt = elt->pn_next) {
         if (elt->isKind(PNK_SPREAD))
             nspread++;
     }
 
-    ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);
+    ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);                    // ARRAY
     if (off < 0)
         return false;
     CheckTypeSet(cx, bce, JSOP_NEWARRAY);
     jsbytecode *pc = bce->code(off);
 
     // For arrays with spread, this is a very pessimistic allocation, the
     // minimum possible final size.
     SET_UINT24(pc, count - nspread);
 
     ParseNode *pn2 = pn;
     jsatomid atomIndex;
     bool afterSpread = false;
     for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
         if (!afterSpread && pn2->isKind(PNK_SPREAD)) {
             afterSpread = true;
-            if (!EmitNumberOp(cx, atomIndex, bce))
+            if (!EmitNumberOp(cx, atomIndex, bce))                       // ARRAY INDEX
                 return false;
         }
         if (!UpdateSourceCoordNotes(cx, bce, pn2->pn_pos.begin))
             return false;
         if (pn2->isKind(PNK_ELISION)) {
             if (Emit1(cx, bce, JSOP_HOLE) < 0)
                 return false;
         } else {
             ParseNode *expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2;
-            if (!EmitTree(cx, bce, expr))
+            if (!EmitTree(cx, bce, expr))                                // ARRAY INDEX? VALUE
                 return false;
         }
         if (pn2->isKind(PNK_SPREAD)) {
-            if (!EmitIterator(cx, bce))
+            if (!EmitIterator(cx, bce))                                  // ARRAY INDEX ITER
+                return false;
+            if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)            // INDEX ITER ARRAY
                 return false;
-            if (!EmitSpread(cx, bce))
+            if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)            // ITER ARRAY INDEX
+                return false;
+            if (!EmitSpread(cx, bce))                                    // ARRAY INDEX
                 return false;
         } else if (afterSpread) {
             if (Emit1(cx, bce, JSOP_INITELEM_INC) < 0)
                 return false;
         } else {
             off = EmitN(cx, bce, JSOP_INITELEM_ARRAY, 3);
             if (off < 0)
                 return false;
             SET_UINT24(bce->code(off), atomIndex);
         }
     }
     JS_ASSERT(atomIndex == count);
     if (afterSpread) {
-        if (Emit1(cx, bce, JSOP_POP) < 0)
+        if (Emit1(cx, bce, JSOP_POP) < 0)                                // ARRAY
             return false;
     }
 
     /* Emit an op to finish the array and aid in decompilation. */
     return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
 }
 
 static bool
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -657,44 +657,35 @@ Parser<ParseHandler>::reportBadReturn(No
     } else {
         errnum = anonerrnum;
     }
     return report(kind, pc->sc->strict, pn, errnum, name.ptr());
 }
 
 /*
  * Check that assigning to lhs is permitted.  Assigning to 'eval' or
- * 'arguments' is banned in strict mode and in destructuring assignment.
+ * 'arguments' is banned in strict mode.
  */
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor)
-{
-    if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment)
+Parser<ParseHandler>::checkStrictAssignment(Node lhs)
+{
+    if (!pc->sc->needStrictChecks())
         return true;
 
     JSAtom *atom = handler.isName(lhs);
     if (!atom)
         return true;
 
     if (atom == context->names().eval || atom == context->names().arguments) {
         JSAutoByteString name;
         if (!AtomToPrintableString(context, atom, &name))
             return false;
 
-        ParseReportKind kind;
-        unsigned errnum;
-        if (pc->sc->strict || flavor != KeyedDestructuringAssignment) {
-            kind = ParseStrictError;
-            errnum = JSMSG_BAD_STRICT_ASSIGN;
-        } else {
-            kind = ParseError;
-            errnum = JSMSG_BAD_DESTRUCT_ASSIGN;
-        }
-        if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr()))
+        if (!report(ParseStrictError, pc->sc->strict, lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
             return false;
     }
     return true;
 }
 
 /*
  * Check that it is permitted to introduce a binding for atom.  Strict mode
  * forbids introducing new definitions for 'eval', 'arguments', or for any
@@ -1052,17 +1043,17 @@ Parser<FullParseHandler>::makeDefIntoUse
     return true;
 }
 
 /*
  * Parameter block types for the several Binder functions.  We use a common
  * helper function signature in order to share code among destructuring and
  * simple variable declaration parsers.  In the destructuring case, the binder
  * function is called indirectly from the variable declaration parser by way
- * of CheckDestructuring and its friends.
+ * of checkDestructuring and its friends.
  */
 
 template <typename ParseHandler>
 struct BindData
 {
     explicit BindData(ExclusiveContext *cx) : let(cx) {}
 
     typedef bool
@@ -3032,90 +3023,100 @@ Parser<FullParseHandler>::bindDestructur
 /*
  * 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.
  *
- * - declaration-like: |var| and |let| declarations, functions' formal
+ * - 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 CheckDestructuring, verify that the
- * tree is a valid destructuring expression.
+ * primaryExpr, and then, here in checkDestructuring, verify that the
+ * tree is a valid AssignmentPattern or BindingPattern.
  *
  * In assignment-like contexts, we parse the pattern with
  * pc->inDeclDestructuring 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.  CheckDestructuring is called with |data| nullptr (since
+ * and so on.  checkDestructuring is called with |data| nullptr (since
  * we won't be binding 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->inDeclDestructuring set, which directs primaryExpr to leave
  * whatever name nodes it creates unconnected.  Then, here in
- * CheckDestructuring, we require the pattern's property value
+ * checkDestructuring, we require the pattern's property value
  * positions to be simple names, and define them as appropriate to the
  * context.  For these calls, |data| points to the right sort of
  * BindData.
- *
- * The 'toplevel' is a private detail of the recursive strategy used by
- * CheckDestructuring and callers should use the default value.
  */
 template <>
 bool
-Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
-                                             ParseNode *left, bool toplevel)
+Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data, ParseNode *left)
 {
     bool ok;
 
     if (left->isKind(PNK_ARRAYCOMP)) {
         report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
     Rooted<StaticBlockObject *> blockObj(context);
     blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr;
 
     if (left->isKind(PNK_ARRAY)) {
-        for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
-            if (!pn->isKind(PNK_ELISION)) {
-                if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
-                    ok = checkDestructuring(data, pn, false);
+        for (ParseNode *element = left->pn_head; element; element = element->pn_next) {
+            if (!element->isKind(PNK_ELISION)) {
+                ParseNode *target = element;
+                if (target->isKind(PNK_SPREAD)) {
+                    if (target->pn_next) {
+                        report(ParseError, false, target->pn_next, JSMSG_PARAMETER_AFTER_REST);
+                        return false;
+                    }
+                    target = target->pn_kid;
+
+                    // The RestElement should not support nested patterns.
+                    if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
+                        report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET);
+                        return false;
+                    }
+                }
+                if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
+                    ok = checkDestructuring(data, target);
                 } else {
                     if (data) {
-                        if (!pn->isKind(PNK_NAME)) {
-                            report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
+                        if (!target->isKind(PNK_NAME)) {
+                            report(ParseError, false, target, JSMSG_NO_VARIABLE_NAME);
                             return false;
                         }
-                        ok = bindDestructuringVar(data, pn);
+                        ok = bindDestructuringVar(data, target);
                     } else {
-                        ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
+                        ok = checkAndMarkAsAssignmentLhs(target, KeyedDestructuringAssignment);
                     }
                 }
                 if (!ok)
                     return false;
             }
         }
     } else {
         JS_ASSERT(left->isKind(PNK_OBJECT));
         for (ParseNode *member = left->pn_head; member; member = member->pn_next) {
             MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
             ParseNode *expr = member->pn_right;
 
             if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
-                ok = checkDestructuring(data, expr, false);
+                ok = checkDestructuring(data, expr);
             } else if (data) {
                 if (!expr->isKind(PNK_NAME)) {
                     report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
                     return false;
                 }
                 ok = bindDestructuringVar(data, expr);
             } else {
                 /*
@@ -3136,18 +3137,17 @@ Parser<FullParseHandler>::checkDestructu
         }
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
-                                               Node left, bool toplevel)
+Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data, Node left)
 {
     return abortIfSyntaxParser();
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt)
 {
@@ -5486,17 +5486,17 @@ Parser<ParseHandler>::condExpr1()
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
-        if (!checkStrictAssignment(pn, flavor))
+        if (!checkStrictAssignment(pn))
             return false;
         if (flavor == KeyedDestructuringAssignment) {
             /*
              * We may be called on a name node that has already been
              * specialized, in the very weird "for (var [x] = i in o) ..."
              * case. See bug 558633.
              */
             if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
@@ -5517,39 +5517,45 @@ Parser<FullParseHandler>::checkAndMarkAs
             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
         if (!checkDestructuring(nullptr, pn))
             return false;
         break;
 
       case PNK_CALL:
+        if (flavor == KeyedDestructuringAssignment) {
+            report(ParseError, false, pn, JSMSG_BAD_DESTRUCT_TARGET);
+            return false;
+        }
         if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
         break;
 
       default:
-        report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
+        unsigned errnum = (flavor == KeyedDestructuringAssignment) ? JSMSG_BAD_DESTRUCT_TARGET :
+            JSMSG_BAD_LEFTSIDE_OF_ASS;
+        report(ParseError, false, pn, errnum);
         return false;
     }
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
 {
     /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
     if (pn != SyntaxParseHandler::NodeName &&
         pn != SyntaxParseHandler::NodeGetProp &&
         pn != SyntaxParseHandler::NodeLValue)
     {
         return abortIfSyntaxParser();
     }
-    return checkStrictAssignment(pn, flavor);
+    return checkStrictAssignment(pn);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr()
 {
     JS_CHECK_RECURSION(context, return null());
 
@@ -5649,17 +5655,17 @@ Parser<FullParseHandler>::checkAndMarkAs
            kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) ||
            kid->isOp(JSOP_FUNCALL) ||
            kid->isOp(JSOP_FUNAPPLY))))
     {
         report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
         return false;
     }
 
-    if (!checkStrictAssignment(kid, IncDecAssignment))
+    if (!checkStrictAssignment(kid))
         return false;
 
     // Mark.
     if (kid->isKind(PNK_NAME)) {
         kid->markAsAssigned();
     } else if (kid->isKind(PNK_CALL)) {
         if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
             return false;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -228,20 +228,20 @@ struct ParseContext : public GenericPars
     // require reparsing the function. In global/module/generator-tail contexts,
     // we don't need to reparse when encountering a DirectivePrologue so this
     // pointer may be nullptr.
     Directives *newDirectives;
 
     // Set when parsing a declaration-like destructuring pattern.  This flag
     // causes PrimaryExpr to create PN_NAME parse nodes for variable references
     // which are not hooked into any definition's use chain, added to any tree
-    // context's AtomList, etc. etc.  CheckDestructuring will do that work
+    // context's AtomList, etc. etc.  checkDestructuring will do that work
     // later.
     //
-    // The comments atop CheckDestructuring explain the distinction between
+    // The comments atop checkDestructuring explain the distinction between
     // assignment-like and declaration-like destructuring patterns, and why
     // they need to be treated differently.
     bool            inDeclDestructuring:1;
 
     ParseContext(Parser<ParseHandler> *prs, GenericParseContext *parent,
                  Node maybeFunction, SharedContext *sc,
                  Directives *newDirectives,
                  unsigned staticLevel, uint32_t bodyid, uint32_t blockScopeDepth)
@@ -610,30 +610,30 @@ class Parser : private JS::AutoGCRooter,
     bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
                                  bool *pbodyProcessed);
     bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
                                 ParseNodeKind headKind);
     bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
-    bool checkStrictAssignment(Node lhs, AssignmentFlavor flavor);
+    bool checkStrictAssignment(Node lhs);
     bool checkStrictBinding(PropertyName *name, Node pn);
     bool defineArg(Node funcpn, HandlePropertyName name,
                    bool disallowDuplicateArgs = false, Node *duplicatedArg = nullptr);
     Node pushLexicalScope(StmtInfoPC *stmt);
     Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
     Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
     bool noteNameUse(HandlePropertyName name, Node pn);
     Node objectLiteral();
     Node arrayInitializer();
     Node newRegExp();
 
     Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars);
-    bool checkDestructuring(BindData<ParseHandler> *data, Node left, bool toplevel = true);
+    bool checkDestructuring(BindData<ParseHandler> *data, Node left);
     bool bindDestructuringVar(BindData<ParseHandler> *data, Node pn);
     bool bindDestructuringLHS(Node pn);
     bool makeSetCall(Node pn, unsigned msg);
     Node cloneLeftHandSide(Node opn);
     Node cloneParseTree(Node opn);
 
     Node newNumber(const Token &tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
--- a/js/src/jit-test/tests/arguments/destructuring-exprbody.js
+++ b/js/src/jit-test/tests/arguments/destructuring-exprbody.js
@@ -1,6 +1,7 @@
 // See bug 763313
 function f([a]) a
 var i = 0;
-var o = {get 0() { i++; return 42; }};
+var o = {'@@iterator': function () { i++; return {
+  next: function () { i++; return {value: 42, done: false}; }}}};
 assertEq(f(o), 42);
-assertEq(i, 1);
+assertEq(i, 2);
--- a/js/src/jit-test/tests/auto-regress/bug532363.js
+++ b/js/src/jit-test/tests/auto-regress/bug532363.js
@@ -1,9 +1,10 @@
 // Binary: cache/js-dbg-32-410468c50ca1-linux
 // Flags: -j
 //
-(function() {
+load(libdir + 'asserts.js');
+assertThrowsInstanceOf(function() {
   for each(z in [0, 0, 0, 0]) { ({
       __parent__: []
     } = [])
   }
-})()
+}, TypeError); // [].__parent__ is undefined
--- a/js/src/jit-test/tests/auto-regress/bug596817.js
+++ b/js/src/jit-test/tests/auto-regress/bug596817.js
@@ -1,4 +1,6 @@
 // Binary: cache/js-dbg-32-a409054e1395-linux
 // Flags: -m
 //
-(function(){for(var[x]=x>>x in[[]<[]]){[]}})()
+load(libdir + 'asserts.js');
+// value is not iterable
+assertThrowsInstanceOf(function(){for(var[x]=x>>x in[[]<[]]){[]}}, TypeError);
--- a/js/src/jit-test/tests/auto-regress/bug785776.js
+++ b/js/src/jit-test/tests/auto-regress/bug785776.js
@@ -1,4 +1,6 @@
 // Binary: cache/js-dbg-64-e08a67884b9b-linux
 // Flags: -m -n -a
 //
-function f([x]){}f(DataView.prototype)
+load(libdir + 'asserts.js');
+// has no @@iterator property
+assertThrowsInstanceOf(() => {function f([x]){}f(DataView.prototype)}, TypeError);
--- a/js/src/jit-test/tests/baseline/bug843429.js
+++ b/js/src/jit-test/tests/baseline/bug843429.js
@@ -1,6 +1,6 @@
 (function () {
-    const x = [] = {};
+    const x = [] = [];
     (function () {
 	String(x);
     })();
 })();
--- a/js/src/jit-test/tests/baseline/bug847678.js
+++ b/js/src/jit-test/tests/baseline/bug847678.js
@@ -1,7 +1,7 @@
-// |jit-test| error: SyntaxError
+// |jit-test| error: TypeError
 s = newGlobal();
 function g(c) {
     evalcx(c, s)
 }
 g("[eval]=(function(){})")
 g("while(eval());")
--- a/js/src/jit-test/tests/baseline/setcall.js
+++ b/js/src/jit-test/tests/baseline/setcall.js
@@ -1,63 +1,32 @@
+load(libdir + 'asserts.js');
+
 var calls = 0;
 function g() {
     calls++;
 };
 function test1() {
     for (var i=0; i<20; i++) {
 	if (i > 18)
 	    g() = 2;
     }
 }
-try {
-    test1();
-    assertEq(0, 1);
-} catch(e) {
-    assertEq(e instanceof ReferenceError, true);
-}
-
+assertThrowsInstanceOf(test1, ReferenceError);
 assertEq(calls, 1);
 
 function test2() {
     for (var i=0; i<20; i++) {
 	if (i > 18)
 	    g()++;
     }
 }
-try {
-    test2();
-    assertEq(0, 1);
-} catch(e) {
-    assertEq(e instanceof ReferenceError, true);
-}
+assertThrowsInstanceOf(test2, ReferenceError);
 assertEq(calls, 2);
 
 function test3() {
-    var v1, v2, v3;
-    var z = [1, 2, 3];
-    for (var i=0; i<15; i++) {
-       if (i > 12)
-           [v1, v2, g(), v3] = z
-    }
-}
-try {
-    test3();
-    assertEq(0, 1);
-} catch(e) {
-    assertEq(e instanceof ReferenceError, true);
-}
-assertEq(calls, 3);
-
-function test4() {
     for (var i=0; i<20; i++) {
 	if (i > 18)
 	    g() >>= 2;
     }
 }
-try {
-    test4();
-    assertEq(0, 1);
-} catch(e) {
-    assertEq(e instanceof ReferenceError, true);
-}
-
-assertEq(calls, 4);
+assertThrowsInstanceOf(test3, ReferenceError);
+assertEq(calls, 3);
--- a/js/src/jit-test/tests/basic/bug685321-1.js
+++ b/js/src/jit-test/tests/basic/bug685321-1.js
@@ -1,11 +1,11 @@
 function f() {
     function g() {
         for (var i = 0; i < 3; i++)
         x = i;
     };
-    var [x] = 0;
+    var [x] = [];
     g();
     assertEq(x, 2);
     print(x);
 }
 f();
--- a/js/src/jit-test/tests/basic/bug685321-2.js
+++ b/js/src/jit-test/tests/basic/bug685321-2.js
@@ -1,13 +1,13 @@
 var o = {};
 function f() {
     function g() {
         x = 80;
         return x;
     };
     Object.defineProperty(o, "f", {get:g});
-    var [x] = 0;
+    var [x] = [];
     x = {};
     2 + o.f;
     print(x);
 }
 f();
--- a/js/src/jit-test/tests/basic/bug778268.js
+++ b/js/src/jit-test/tests/basic/bug778268.js
@@ -1,3 +1,3 @@
 // Just don't assert
 j : 4;
-let [j] = 1, j;
+let [j] = [], j;
--- a/js/src/jit-test/tests/basic/bug862228.js
+++ b/js/src/jit-test/tests/basic/bug862228.js
@@ -1,3 +1,3 @@
-// |jit-test| error: ReferenceError
+// |jit-test| error: SyntaxError
 
 ({"":y=""}=
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/destructuring-iterator.js
@@ -0,0 +1,109 @@
+
+load(libdir + 'asserts.js');
+load(libdir + 'iteration.js');
+load(libdir + 'eqArrayHelper.js');
+
+// throw on non-iterables
+assertThrowsInstanceOf(() => { [a, b, c] = {0: 0, 1: 1, 2: 2} }, TypeError);
+
+var nextcalls = 0, donecalls = 0, valuecalls = 0;
+var doneafter = 0;
+var iterable = {};
+iterable[std_iterator] = function () {
+  return {
+    next: function () {
+      assertEq(arguments.length, 0, 'iterator.next() should be called with no arguments');
+      nextcalls++;
+      return {
+        get done() {
+          donecalls++;
+          return --doneafter < 0;
+        },
+        get value() {
+          valuecalls++;
+          return valuecalls;
+        }
+      };
+    }
+  }
+};
+
+function assertIterable(expectCalls, fn, expectResult) {
+  [nextcalls, donecalls, valuecalls, doneafter] = [0,0,0, expectCalls[3]];
+  assertEqArray(fn(iterable), expectResult);
+  assertEq(nextcalls,  expectCalls[0], 'calls to iterator.next()');
+  assertEq(donecalls,  expectCalls[1], 'getting iterator.next().done');
+  assertEq(valuecalls, expectCalls[2], 'getting iterator.next().value');
+}
+
+assertIterable([1,1,1,1],
+  it => { var [a] = it; return [a]; },
+  [1]);
+assertIterable([3,3,1,1],
+  it => { var [a,b,c] = it; return [a,b,c]; },
+  [1,undefined,undefined]);
+assertIterable([3,3,1,1],
+  it => { var [a,b,...rest] = it; return [a,b,...rest]; },
+  [1,undefined]);
+assertIterable([5,5,4,4],
+  it => { var [,,...rest] = it; return rest; },
+  [3,4]);
+
+var arraycalls = 0;
+var ArrayIterator = Array.prototype[std_iterator];
+Array.prototype[std_iterator] = function () {
+  arraycalls++;
+  return ArrayIterator.apply(this, arguments);
+};
+// [...rest] should not call Array#@@iterator for the LHS
+var [...rest] = iterable;
+assertEq(arraycalls, 0, 'calls to Array#@@iterator');
+
+
+// loop `fn` a few times, to get it JIT-compiled
+function loop(fn) {
+  var i = 1e4;
+  while (i--) fn();
+}
+
+loop(() => { doneafter = 4; var [a] = iterable; return a; });
+loop(() => { doneafter = 4; var [a,b,...rest] = iterable; return rest; });
+
+
+// destructuring assignment should always use iterators and not optimize
+// to a "group assignment"
+delete Array.prototype[std_iterator];
+assertThrowsInstanceOf(() => { var [a,b] = [1,2]; }, TypeError);
+Array.prototype[std_iterator] = ArrayIterator;
+
+// observe the binding order
+a = undefined, b = undefined, c = undefined;
+var obj = {};
+obj[std_iterator] = function* () {
+	// normal fields should be initialized right after |.next()|
+	yield 1;
+	assertEq(a, 1);
+	yield 2;
+	yield 3;
+	assertEq(b, 3);
+	yield 4;
+	yield 5;
+	// rest should be initialized after the iterator is exhausted
+	assertEq(c, undefined);
+};
+[a, , b, ...c] = obj;
+assertEqArray(c, [4,5]);
+
+// a throw inside the destructuring of the "catch" value should not enter
+// the "catch" block
+
+assertThrowsValue(function () {
+  try {
+    Array.prototype[std_iterator] = function () { throw 'from iterator'; };
+    throw [1, 2];
+  } catch ([x, y]) {
+    throw 'not reached';
+  }
+}, 'from iterator');
+Array.prototype[std_iterator] = ArrayIterator;
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/destructuring-rest-identifiers.js
@@ -0,0 +1,73 @@
+
+load(libdir + 'asserts.js');
+load(libdir + 'eqArrayHelper.js');
+
+var reserved = [
+  'break',
+  'do',
+  'in',
+  'typeof',
+  'case',
+  'else',
+  'instanceof',
+  'var',
+  'catch',
+  'export',
+  'new',
+  'void',
+  'class',
+  'extends',
+  'return',
+  'while',
+  'const',
+  'finally',
+  'super',
+  'with',
+  'continue',
+  'for',
+  'switch',
+  'debugger',
+  'function',
+  'this',
+  'delete',
+  'import',
+  'try',
+  'enum',
+  'null',
+  'true',
+  'false'
+];
+reserved.forEach(ident => {
+  assertThrowsInstanceOf(() => new Function('var [...' + ident + '] = []'), SyntaxError);
+});
+
+var strictIdentifiers = [
+  // XXX: see bug 1032150. Once fixed, please uncomment these values and
+  // remove the assertions below
+  //'yield',
+  //'let',
+  'eval',
+  'arguments',
+  'implements',
+  'interface',
+  'package',
+  'private',
+  'protected',
+  'public',
+  'static'
+];
+assertThrowsInstanceOf(() => new Function('[...yield] = []'), SyntaxError);
+assertThrowsInstanceOf(() => new Function('[...let] = []'), SyntaxError);
+
+strictIdentifiers.forEach(ident =>
+  assertThrowsInstanceOf(() =>
+    new Function('"use strict"; [...' + ident + '] = []'), SyntaxError));
+
+var globalEval = eval;
+strictIdentifiers.forEach(ident => {
+  globalEval(ident + ' = null');
+  assertEqArray(new Function('input', '[, ...' + ident + '] = input;' +
+    'return ' + ident
+  )([1, 2, 3]), [2, 3]);
+});
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/destructuring-rest.js
@@ -0,0 +1,134 @@
+
+load(libdir + 'asserts.js');
+load(libdir + 'eqArrayHelper.js');
+
+assertThrowsInstanceOf(() => new Function('[...a, ,] = []'), SyntaxError, 'trailing elision');
+assertThrowsInstanceOf(() => new Function('[a, ...b, c] = []'), SyntaxError, 'trailing param');
+assertThrowsInstanceOf(() => new Function('[...[a]] = []'), SyntaxError, 'nested arraypattern');
+assertThrowsInstanceOf(() => new Function('[...{a}] = []'), SyntaxError, 'nested objectpattern');
+assertThrowsInstanceOf(() => new Function('[...a=b] = []'), SyntaxError, 'assignment expression');
+assertThrowsInstanceOf(() => new Function('[...a()] = []'), SyntaxError, 'call expression');
+assertThrowsInstanceOf(() => new Function('[...(a,b)] = []'), SyntaxError, 'comma expression');
+assertThrowsInstanceOf(() => new Function('[...a++] = []'), SyntaxError, 'postfix expression');
+assertThrowsInstanceOf(() => new Function('[...!a] = []'), SyntaxError, 'unary expression');
+assertThrowsInstanceOf(() => new Function('[...a+b] = []'), SyntaxError, 'binary expression');
+assertThrowsInstanceOf(() => new Function('var [...a.x] = []'), SyntaxError, 'lvalue expression in declaration');
+
+// XXX: The way the current parser works, certain things, like a trailing comma
+// and parenthesis, are lost before we check for destructuring.
+// See bug 1041341. Once fixed, please update these assertions
+assertThrowsInstanceOf(() =>
+	assertThrowsInstanceOf(() => new Function('[...b,] = []'), SyntaxError)
+	, Error);
+assertThrowsInstanceOf(() =>
+	assertThrowsInstanceOf(() => new Function('var [...(b)] = []'), SyntaxError)
+	, Error);
+
+var inputArray = [1, 2, 3];
+var inputDeep = [1, inputArray];
+var inputObject = {a: inputArray};
+function *inputGenerator() {
+  yield 1;
+  yield 2;
+  yield 3;
+}
+
+var o = {prop: null, call: function () { return o; }};
+var globalEval = eval;
+
+var expected = [2, 3];
+
+function testAll(fn) {
+  testDeclaration(fn);
+
+  o.prop = null;
+  assertEqArray(fn('[, ...(o.prop)]', inputArray, 'o.prop'), expected);
+  o.prop = null;
+  assertEqArray(fn('[, ...(o.call().prop)]', inputArray, 'o.prop'), expected);
+}
+function testDeclaration(fn) {
+  assertEqArray(fn('[, ...rest]', inputArray), expected);
+  assertEqArray(fn('[, ...rest]', inputGenerator()), expected);
+  assertEqArray(fn('[, [, ...rest]]', inputDeep), expected);
+  assertEqArray(fn('{a: [, ...rest]}', inputObject), expected);
+}
+
+function testVar(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'var ' + pattern + ' = input;' +
+    'return ' + binding
+  )(input);
+}
+testDeclaration(testVar);
+
+function testGlobal(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    '(' + pattern + ') = input;' +
+    'return ' + binding
+  )(input);
+}
+testAll(testGlobal);
+
+function testClosure(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'var ' + binding + '; (function () {' +
+    '(' + pattern + ') = input;' +
+    '})();' +
+    'return ' + binding
+  )(input);
+}
+testDeclaration(testClosure);
+
+function testArgument(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'return (function (' + pattern + ') {' +
+    'return ' + binding + '; })(input);'
+  )(input);
+}
+testDeclaration(testArgument);
+
+function testArgumentFunction(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function(pattern,
+    'return ' + binding
+  )(input);
+}
+// XXX: ES6 requires the `Function` constructor to accept arbitrary
+// `BindingElement`s as formal parameters. See Bug 1037939.
+// Once fixed, please update the assertions below.
+assertThrowsInstanceOf(() => testDeclaration(testArgumentFunction), SyntaxError);
+
+function testThrow(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'try { throw input }' +
+    'catch(' + pattern + ') {' +
+    'return ' + binding + '; }'
+  )(input);
+}
+testDeclaration(testThrow);
+
+// XXX: Support for let blocks and expressions will be removed in bug 1023609.
+// However, they test a special code path in destructuring assignment so having
+// these tests here for now seems like a good idea.
+function testLetBlock(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'let (' + pattern + ' = input)' +
+    '{ return ' + binding + '; }'
+  )(input);
+}
+testDeclaration(testLetBlock);
+
+function testLetExpression(pattern, input, binding) {
+  binding = binding || 'rest';
+  return new Function('input',
+    'return (let (' + pattern + ' = input) ' + binding + ');'
+  )(input);
+}
+testDeclaration(testLetExpression);
+
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -44,17 +44,17 @@ function check(expr, expected=expr) {
             Function("var v1, v2; let (o, undef) { " + statement + " }"),
             // Shadowed let block
             Function("o", "undef", "let (o, undef) { " + statement + " }"),
             // Let in a switch
             Function("var x = 4; switch (x) { case 4: let o, undef;" + statement + "\ncase 6: break;}"),
             // The more lets the merrier
             Function("let (x=4, y=5) { x + y; }\nlet (a, b, c) { a + b - c; }\nlet (o, undef) {" + statement + " }"),
             // Let destructuring
-            Function("o", "undef", "let ([] = 4) {} let (o, undef) { " + statement + " }"),
+            Function("o", "undef", "let ([] = []) {} let (o, undef) { " + statement + " }"),
             // Try-catch blocks
             Function("o", "undef", "try { let q = 4; try { let p = 4; } catch (e) {} } catch (e) {} let (o, undef) { " + statement + " }")
         ];
 
         try {
             // Let in for-in
             check_one(expected,
                       Function("var undef, o; for (let z in [1, 2]) { " + statement + " }"),
@@ -104,13 +104,13 @@ for (let tok of ["|", "^", "&", "==", "!
 check("o[!(o)]");
 check("o[~(o)]");
 check("o[+ (o)]");
 check("o[- (o)]");
 
 // A few one off tests
 check_one("6", (function () { 6() }), " is not a function");
 check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
-check_one("(intermediate value)[0]", function () { var [{ x }] = [null, {}]; }, " is null");
-check_one("(intermediate value)[2]", function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
+check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { var [{ x }] = [null, {}]; }, " is null");
+check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
 
 // Check fallback behavior
 assertThrowsInstanceOf(function () { for (let x of undefined) {} }, TypeError);
--- a/js/src/jit-test/tests/basic/spread-call-setcall.js
+++ b/js/src/jit-test/tests/basic/spread-call-setcall.js
@@ -6,21 +6,24 @@ function g() {
 let a = {
   g: function() {
   }
 };
 
 function check(expr) {
   assertThrowsInstanceOf(Function(expr), ReferenceError);
 }
+function checkDestructuring(expr) {
+  assertThrowsInstanceOf(() => Function(expr), SyntaxError);
+}
 
 check("g(...[]) = 1");
 check("a.g(...[]) = 1");
 check("eval(...['1']) = 1");
-check("[g(...[])] = 1");
-check("[a.g(...[])] = 1");
-check("[eval(...['1'])] = 1");
-check("({y: g(...[])}) = 1");
-check("({y: a.g(...[])}) = 1");
-check("({y: eval(...['1'])}) = 1");
 check("g(...[]) ++");
 check("a.g(...[]) ++");
 check("eval(...['1']) ++");
+checkDestructuring("[g(...[])] = []");
+checkDestructuring("[a.g(...[])] = []");
+checkDestructuring("[eval(...['1'])] = []");
+checkDestructuring("({y: g(...[])}) = 1");
+checkDestructuring("({y: a.g(...[])}) = 1");
+checkDestructuring("({y: eval(...['1'])}) = 1");
--- a/js/src/jit-test/tests/basic/testBug714650.js
+++ b/js/src/jit-test/tests/basic/testBug714650.js
@@ -1,36 +1,36 @@
-let ([] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1,
-     [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1, [] = 1)
+let ([] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [],
+     [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [], [] = [])
 {
     print("ok");
 }
--- a/js/src/jit-test/tests/basic/testLet.js
+++ b/js/src/jit-test/tests/basic/testLet.js
@@ -47,51 +47,43 @@ function isError(str)
 // let expr
 test('return let (y) x;');
 test('return let (x) "" + x;', 'unicorns', 'undefined');
 test('return let (y = x) (y++, "" + y);', 'unicorns', 'NaN');
 test('return let (y = 1) (y = x, y);');
 test('return let ([] = x) x;');
 test('return let (x = {a: x}) x.a;');
 test('return let ({a: x} = {a: x}) x;');
-test('return let ([x] = {0: x}) x;');
+test('return let ([x] = [x]) x;');
 test('return let ({0: x} = [x]) x;');
-test('return let ({0: []} = []) x;');
+test('return let ({0: []} = [[]]) x;');
 test('return let ([, ] = x) x;');
 test('return let ([, , , , ] = x) x;');
-test('return let ([[]] = x) x;');
-test('return let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) x;');
-test('return let ([[], []] = x) x;');
-test('return let ([[[[]]], [], , [], [[]]] = x) x;');
-test('return let ({x: []} = x) x;');
-test('return let ({x: [], y: {x: []}} = x) "ponies";', {y:{}});
-test('return let ({x: []} = x, [{x: []}] = x) "ponies";');
 test('return let (x = x) x;');
 test('return let (x = eval("x")) x;');
 test('return let (x = (let (x = x + 1) x) + 1) x;', 1, 3);
 test('return let (x = (let (x = eval("x") + 1) eval("x")) + 1) eval("x");', 1, 3);
 test('return let (x = x + 1, y = x) y;');
-test('return let (x = x + 1, [] = x, [[, , ]] = x, y = x) y;');
+test('return let (x = x + 1, [] = x, /*[[, , ]] = x, */y = x) y;');
 test('return let ([{a: x}] = x, [, {b: y}] = x) let (x = x + 1, y = y + 2) x + y;', [{a:"p"},{b:"p"}], "p1p2");
 test('return let ([] = []) x;');
 test('return let ([] = [x]) x;');
 test('return let ([a] = (1, [x])) a;');
 test('return let ([a] = (1, x, 1, x)) a;', ['ponies']);
 test('return let ([x] = [x]) x;');
 test('return let ([[a, [b, c]]] = [[x, []]]) a;');
 test('return let ([x, y] = [x, x + 1]) x + y;', 1, 3);
 test('return let ([x, y, z] = [x, x + 1, x + 2]) x + y + z;', 1, 6);
 test('return let ([[x]] = [[x]]) x;');
 test('return let ([x, y] = [x, x + 1]) x;');
 test('return let ([x, [y, z]] = [x, x + 1]) x;');
 test('return let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) x;',{x:['ponies']});
 test('return let (x = (3, x)) x;');
 test('return let (x = x + "s") x;', 'ponie');
 test('return let ([x] = (3, [x])) x;');
-test('return let ([] = [[]] = {}) x;');
 test('return let (y = x) function () {return eval("y");}();');
 test('return eval("let (y = x) y");');
 test('return let (y = x) (eval("var y = 2"), y);', 'ponies', 2);
 test('"use strict";return let (y = x) (eval("var y = 2"), y);');
 test('this.y = x;return let (y = 1) this.eval("y");');
 test('try {let (x = x) eval("throw x");} catch (e) {return e;}');
 test('try {return let (x = eval("throw x")) x;} catch (e) {return e;}');
 isError('let (x = 1, x = 2) x');
@@ -105,28 +97,21 @@ isError('(let (x = function() { return x
 test('let (y) {return x;}');
 test('let (y = x) {y++;return "" + y;}', 'unicorns', 'NaN');
 test('let (y = 1) {y = x;return y;}');
 test('let (x) {return "" + x;}', 'unicorns', 'undefined');
 test('let ([] = x) {return x;}');
 test('let (x) {}return x;');
 test('let (x = {a: x}) {return x.a;}');
 test('let ({a: x} = {a: x}) {return x;}');
-test('let ([x] = {0: x}) {return x;}');
+test('let ([x] = [x]) {return x;}');
 test('let ({0: x} = [x]) {return x;}');
-test('let ({0: []} = []) {return x;}');
+test('let ({0: []} = [[]]) {return x;}');
 test('let ([, ] = x) {return x;}');
 test('let ([, , , , ] = x) {return x;}');
-test('let ([[]] = x) {return x;}');
-test('let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) {return x;}');
-test('let ([[], []] = x) {return x;}');
-test('let ([[[[]]], [], , [], [[]]] = x) {return x;}');
-test('let ({x: []} = x) {return x;}');
-test('let ({x: [], y: {x: []}} = x) {return "ponies";}', {y:{}});
-test('let ({x: []} = x, [{x: []}] = x) {return "ponies";}');
 test('let (x = x) {return x;}');
 test('let (x = eval("x")) {return x;}');
 test('let (x = (let (x = x + 1) x) + 1) {return x;}', 1, 3);
 test('let (x = (let (x = eval("x") + 1) eval("x")) + 1) {return eval("x");}', 1, 3);
 test('let (x = x + 1, y = x) {return y;}');
 test('let (x = x + 1, [] = x, [[, , ]] = x, y = x) {return y;}');
 test('let ([{a: x}] = x, [, {b: y}] = x) {let (x = x + 1, y = y + 2) {return x + y;}}', [{a:"p"},{b:"p"}], "p1p2");
 test('let ([] = []) {return x;}');
@@ -138,40 +123,32 @@ test('let ([[a, [b, c]]] = [[x, []]]) {r
 test('let ([x, y] = [x, x + 1]) {return x + y;}', 1, 3);
 test('let ([x, y, z] = [x, x + 1, x + 2]) {return x + y + z;}', 1, 6);
 test('let ([[x]] = [[x]]) {return x;}');
 test('let ([x, y] = [x, x + 1]) {return x;}');
 test('let ([x, [y, z]] = [x, x + 1]) {return x;}');
 test('let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) {return x;}',{x:['ponies']});
 test('let (y = x[1]) {let (x = x[0]) {try {let (y = "unicorns") {throw y;}} catch (e) {return x + y;}}}', ['pon','ies']);
 test('let (x = x) {try {let (x = "unicorns") eval("throw x");} catch (e) {return x;}}');
-test('let ([] = [[]] = {}) {return x;}');
 test('let (y = x) {return function () {return eval("y");}();}');
 test('return eval("let (y = x) {y;}");');
 test('let (y = x) {eval("var y = 2");return y;}', 'ponies', 2);
 test('"use strict";let (y = x) {eval("var y = 2");return y;}');
 test('this.y = x;let (y = 1) {return this.eval("y");}');
 isError('let (x = 1, x = 2) {x}');
 isError('let ([x, y] = a, {a:x} = b) {x}');
 isError('let ([x, y, x] = a) {x}');
 isError('let ([x, [y, [x]]] = a) {x}');
 
 // var declarations
 test('var y;return x;');
 test('var y = x;return x;');
 test('var [] = x;return x;');
 test('var [, ] = x;return x;');
 test('var [, , , , ] = x;return x;');
-test('var [[]] = x;return x;');
-test('var [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;');
-test('var [[], []] = x;return x;');
-test('var [[[[]]], [], , [], [[]]] = x;return x;');
-test('var {x: []} = x;return x;');
-test('var {x: [], y: {x: []}} = x;return "ponies";', {y:{}});
-test('var {x: []} = x, [{x: []}] = x;return "ponies";');
 test('var x = x;return x;');
 test('var y = y;return "" + y;', 'unicorns', 'undefined');
 test('var x = eval("x");return x;');
 test('var x = (let (x = x + 1) x) + 1;return x;', 1, 3);
 test('var x = (let (x = eval("x") + 1) eval("x")) + 1;return eval("x");', 1, 3);
 test('var X = x + 1, y = x;return y;');
 test('var X = x + 1, [] = X, [[, , ]] = X, y = x;return y;');
 test('var [{a: X}] = x, [, {b: y}] = x;var X = X + 1, y = y + 2;return X + y;', [{a:"p"},{b:"p"}], "p1p2");
@@ -181,38 +158,30 @@ test('var [y] = [x];return y;');
 test('var [a] = (1, [x]);return a;');
 test('var [a] = (1, x, 1, x);return a;', ['ponies']);
 test('var [x, y] = [x, x + 1];return x + y;', 1, 3);
 test('var [x, y, z] = [x, x + 1, x + 2];return x + y + z;', 1, 6);
 test('var [[x]] = [[x]];return x;');
 test('var [x, y] = [x, x + 1];return x;');
 test('var [x, [y, z]] = [x, x + 1];return x;');
 test('var [{x: [x]}, {y1: y, z1: z}] = [x, x + 1];return x;',{x:['ponies']});
-test('var [] = [[]] = {};return x;');
 test('if (x) {var y = x;return x;}');
 test('if (x) {y = x;var y = y;return y;}');
 test('if (x) {var z = y;var [y] = x;z += y;}return z;', ['-'], 'undefined-');
 
 // let declaration in context
 test('if (x) {let y;return x;}');
 test('if (x) {let x;return "" + x;}', 'unicorns', 'undefined');
 test('if (x) {let y = x;return x;}');
 test('if (x) {y = x;let y = y;return y;}');
 test('if (x) {var z = y;let [y] = x;z += y;}return z;', ['-'], 'undefined-');
 test('if (x) {let y = x;return x;}');
 test('if (x) {let [] = x;return x;}');
 test('if (x) {let [, ] = x;return x;}');
 test('if (x) {let [, , , , ] = x;return x;}');
-test('if (x) {let [[]] = x;return x;}');
-test('if (x) {let [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;}');
-test('if (x) {let [[], []] = x;return x;}');
-test('if (x) {let [[[[]]], [], , [], [[]]] = x;return x;}');
-test('if (x) {let {x: []} = x;return x;}');
-test('if (x) {let {x: [], y: {x: []}} = x;return "ponies";}', {y:{}});
-test('if (x) {let {x: []} = x, [{x: []}] = x;return "ponies";}');
 test('if (x) {let x = x;return "" + x;}', 'unicorns', 'undefined');
 test('if (x) {let y = y;return "" + y;}', 'unicorns', 'undefined');
 test('if (x) {let x = eval("x");return "" + x;}', 'unicorns', 'undefined');
 test('if (x) {let y = (let (x = x + 1) x) + 1;return y;}', 1, 3);
 test('if (x) {let y = (let (x = eval("x") + 1) eval("x")) + 1;return eval("y");}', 1, 3);
 test('if (x) {let X = x + 1, y = x;return y;}');
 test('if (x) {let X = x + 1, [] = X, [[, , ]] = X, y = x;return y;}');
 test('if (x) {let [{a: X}] = x, [, {b: Y}] = x;var XX = X + 1, YY = Y + 2;return XX + YY;}', [{a:"p"},{b:"p"}], "p1p2");
@@ -223,17 +192,16 @@ test('if (x) {let [a] = (1, [x]);return 
 test('if (x) {let [a] = (1, x, 1, x);return a;}', ['ponies']);
 test('if (x) {let [X, y] = [x, x + 1];return X + y;}', 1, 3);
 test('if (x) {let [X, y, z] = [x, x + 1, x + 2];return X + y + z;}', 1, 6);
 test('if (x) {let [[X]] = [[x]];return X;}');
 test('if (x) {let [X, y] = [x, x + 1];return X;}');
 test('if (x) {let [X, [y, z]] = [x, x + 1];return X;}');
 test('if (x) {let [{x: [X]}, {y1: y, z1: z}] = [x, x + 1];return X;}',{x:['ponies']});
 test('if (x) {let y = x;try {let x = 1;throw 2;} catch (e) {return y;}}');
-test('if (x) {let [] = [[]] = {};return x;}');
 test('let (y, [] = x) {}try {let a = b(), b;} catch (e) {return x;}');
 test('try {let x = 1;throw 2;} catch (e) {return x;}');
 test('let (y = x) {let x;return y;}');
 test('let (y = x) {let x = y;return x;}');
 test('let ([y, z] = x) {let a = x, b = y;return a;}');
 test('let ([y, z] = x, a = x, [] = x) {let b = x, c = y;return a;}');
 test('function f() {return unicorns;}try {let (x = 1) {let a, b;f();}} catch (e) {return x;}');
 test('function f() {return unicorns;}try {let (x = 1) {let a, b;}f();} catch (e) {return x;}');
@@ -253,45 +221,41 @@ isError('if (x) {let ([x, [y, [x]]] = a)
 isError('let ([x, y] = x) {let x;}');
 
 // for(;;)
 test('for (;;) {return x;}');
 test('for (let y = 1;;) {return x;}');
 test('for (let y = 1;; ++y) {return x;}');
 test('for (let y = 1; ++y;) {return x;}');
 test('for (let (x = 1) x; x != 1; ++x) {return x;}');
-test('for (let [, {a: [], b: []}] = x, [] = x; x;) {return x;}');
-test('for (let x = 1, [y, z] = x, a = x; z < 4; ++z) {return x + y;}', [2,3], 3);
 test('for (let (x = 1, [{a: b, c: d}] = [{a: 1, c: 2}]) x; x != 1; ++x) {return x;}');
 test('for (let [[a, [b, c]]] = [[x, []]];;) {return a;}');
 test('var sum = 0;for (let y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6);
 test('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;', 1, 6);
 test('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;', 1, 1);
 test('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;', 1, 6);
 test('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
 test('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
 test('for (var y = 1;;) {return x;}');
 test('for (var y = 1;; ++y) {return x;}');
 test('for (var y = 1; ++y;) {return x;}');
-test('for (var [, {a: [], b: []}] = x, [] = x; x;) {return x;}');
 test('for (var X = 1, [y, z] = x, a = x; z < 4; ++z) {return X + y;}', [2,3], 3);
 test('var sum = 0;for (var y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6);
 test('var sum = 0;for (var X = x, y = 10; X < 4; ++X) {sum += X;}return sum;', 1, 6);
 test('var sum = 0;for (var X = x; X < 4; ++X) {sum += X;}return x;', 1, 1);
 test('var sum = 0;for (var X = eval("x"); X < 4; ++X) {sum += X;}return sum;', 1, 6);
 test('var sum = 0;for (var X = x; eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
 test('var sum = 0;for (var X = eval("x"); eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
 test('try {for (let x = eval("throw x");;) {}} catch (e) {return e;}');
 test('try {for (let x = x + "s"; eval("throw x");) {}} catch (e) {return e;}', 'ponie');
 test('for (let y = x;;) {let x;return y;}');
 test('for (let y = x;;) {let y;return x;}');
 test('for (let y;;) {let y;return x;}');
 test('for (let a = x;;) {let c = x, d = x;return c;}');
 test('for (let [a, b] = x;;) {let c = x, d = x;return c;}');
-test('for (let [] = [[]] = {};;) {return x;}');
 test('for (let [a] = (1, [x]);;) {return a;}');
 test('for (let [a] = (1, x, 1, x);;) {return a;}', ['ponies']);
 isError('for (let x = 1, x = 2;;) {}');
 isError('for (let [x, y] = a, {a:x} = b;;) {}');
 isError('for (let [x, y, x] = a;;) {}');
 isError('for (let [x, [y, [x]]] = a;;) {}');
 
 // for(in)
--- a/js/src/jit-test/tests/basic/testNonStubGetter.js
+++ b/js/src/jit-test/tests/basic/testNonStubGetter.js
@@ -1,7 +1,7 @@
 function testNonStubGetter() {
-    let ([] = false) { (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); };
+    let ([] = []) { (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); };
     (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })();
     this.unwatch("x");
     return "ok";
 }
 assertEq(testNonStubGetter(), "ok");
--- a/js/src/jit-test/tests/closures/bug540348.js
+++ b/js/src/jit-test/tests/closures/bug540348.js
@@ -1,3 +1,3 @@
 (function() {
- for (var [e] = 1 in (eval("for (b = 0; b < 6; ++b) gc()"))) {}
+ for (var [e] = [] in (eval("for (b = 0; b < 6; ++b) gc()"))) {}
 })()
--- a/js/src/jit-test/tests/ion/bug743099.js
+++ b/js/src/jit-test/tests/ion/bug743099.js
@@ -1,9 +1,9 @@
 function Day( t ) {}
 function WeekDay( t ) {
   var weekday = (Day(t)+4) % 7;
   return( weekday < 0 ? 7 + weekday : weekday );
 }
 var expect = 'No Error';
 for (var i = 0; i < 50; i++) {
-    var [] = expect ? WeekDay( i.a ) : true, uneval;
+    var [] = [expect ? WeekDay(i.a) : true], uneval;
 }
--- a/js/src/jit-test/tests/ion/bug821794.js
+++ b/js/src/jit-test/tests/ion/bug821794.js
@@ -1,10 +1,10 @@
 
 gczeal(2);
 function bitsinbyte() {
-    var [ summary  ]  = true;
+    var summary = true[0];
 }
 function TimeFunc(func) {
     for(var y=0; y<11000; y++)
         func();
 }
 TimeFunc(bitsinbyte);
--- a/js/src/jit-test/tests/jaeger/bug588363-1.js
+++ b/js/src/jit-test/tests/jaeger/bug588363-1.js
@@ -1,6 +1,6 @@
-// |jit-test| error: SyntaxError
+// |jit-test|
 ({eval} = Object.defineProperty(evalcx("lazy"), "", {}))
 eval("eval(/x/)", [])
 
 /* Don't assert or crash. */
 
--- a/js/src/jit-test/tests/parser/bug-889628.js
+++ b/js/src/jit-test/tests/parser/bug-889628.js
@@ -1,38 +1,33 @@
 // Destructuring assignment to eval or arguments in destructuring is a SyntaxError
+// in strict mode
 
 load(libdir + "asserts.js");
 
 var patterns = [
     "[_]",
     "[a, b, _]",
     "[[_]]",
     "[[], [{}, [_]]]",
     "{x:_}",
     "{x:y, z:_}",
     "{0:_}",
     "{_}",
-    //"[..._]"
+    "[..._]"
 ];
 
-// If the assertion below fails, congratulations! It means you have added
-// spread operator support to destructuring assignment. Simply uncomment the
-// "[..._]" case above. Then delete this comment and assertion.
-assertThrowsInstanceOf(() => Function("[...x] = [1]"), ReferenceError);
-
 for (var pattern of patterns) {
     var stmt = pattern + " = obj";
     if (stmt[0] == "{")
         stmt = "(" + stmt + ")";
     stmt += ";"
 
     // stmt is a legal statement...
     Function(stmt);
 
     // ...but not if you replace _ with one of these two names.
     for (var name of ["eval", "arguments"]) {
         var s = stmt.replace("_", name);
-        assertThrowsInstanceOf(() => Function(s), SyntaxError);
-        assertThrowsInstanceOf(() => eval(s), SyntaxError);
-        assertThrowsInstanceOf(() => eval("'use strict'; " + s), SyntaxError);
+        Function(s);
+        assertThrowsInstanceOf(() => Function("'use strict'; " + s), SyntaxError);
     }
 }
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -176,17 +176,17 @@ MSG_DEF(JSMSG_ARGUMENTS_AND_REST,      0
 MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE,     0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
 MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG,      0, JSEXN_INTERNALERR, "array initialiser too large")
 MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD,  1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
 MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,          0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_BAD_BINDING,             1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
 MSG_DEF(JSMSG_BAD_CONTINUE,            0, JSEXN_SYNTAXERR, "continue must be inside loop")
 MSG_DEF(JSMSG_BAD_DESTRUCT_ASS,        0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
-MSG_DEF(JSMSG_BAD_DESTRUCT_ASSIGN,     1, JSEXN_SYNTAXERR, "can't assign to {0} using destructuring assignment")
+MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET,     0, JSEXN_SYNTAXERR, "invalid destructuring target")
 MSG_DEF(JSMSG_BAD_DESTRUCT_DECL,       0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
 MSG_DEF(JSMSG_BAD_DUP_ARGS,            0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,       0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,        0, JSEXN_SYNTAXERR, "invalid for/in left-hand side")
 MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    1, JSEXN_TYPEERR, "generator function {0} returns a value")
 MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,    1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,         1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,       0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3092,16 +3092,24 @@ ASTSerializer::arrayPattern(ParseNode *p
 
     NodeVector elts(cx);
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
         if (next->isKind(PNK_ELISION)) {
             elts.infallibleAppend(NullValue());
+        } else if (next->isKind(PNK_SPREAD)) {
+            RootedValue target(cx);
+            RootedValue spread(cx);
+            if (!pattern(next->pn_kid, pkind, &target))
+                return false;
+            if(!builder.spreadExpression(target, &next->pn_pos, &spread))
+                return false;
+            elts.infallibleAppend(spread);
         } else {
             RootedValue patt(cx);
             if (!pattern(next, pkind, &patt))
                 return false;
             elts.infallibleAppend(patt);
         }
     }
 
--- a/js/src/tests/js1_5/extensions/regress-469625.js
+++ b/js/src/tests/js1_5/extensions/regress-469625.js
@@ -18,16 +18,16 @@ function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   jit(true);
 
   [].__proto__[0] = 'a';
-  for (var j = 0; j < 3; ++j) [[, ]] = [];
+  for (var j = 0; j < 3; ++j) [[, ]] = [,];
 
   jit(false);
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_7/extensions/regress-346642-06.js
+++ b/js/src/tests/js1_7/extensions/regress-346642-06.js
@@ -52,23 +52,18 @@ function test()
   actual = '';
   f = function () { return { set x([a]) { yield; } } }
   var obj = f();
   uneval(obj); actual = 3;
   reportCompare(expect, actual, summary + ': 4');
 
   expect = 3;
   actual = '';
-  "" + function () { [y([a]=b)] = z }; actual = 3;
+  "" + function () { for(;; ([[,]] = p)) { } }; actual = 3;
   reportCompare(expect, actual, summary + ': 5');
 
   expect = 3;
   actual = '';
-  "" + function () { for(;; ([[,]] = p)) { } }; actual = 3;
+  actual = 1; try {for(x in (function ([y]) { })() ) { }}catch(ex){} actual = 3;
   reportCompare(expect, actual, summary + ': 6');
 
-  expect = 3;
-  actual = '';
-  actual = 1; try {for(x in (function ([y]) { })() ) { }}catch(ex){} actual = 3;
-  reportCompare(expect, actual, summary + ': 7');
-
   exitFunc ('test');
 }
--- a/js/src/tests/js1_7/extensions/regress-351102-03.js
+++ b/js/src/tests/js1_7/extensions/regress-351102-03.js
@@ -22,17 +22,17 @@ function test()
  
   var f;
   f = function()
     {
       try
       {
         d.d.d;
       }
-      catch([] if gc())
+      catch({} if gc())
       {
       }
       catch(y)
       {
       }
     };
 
   f();
--- a/js/src/tests/js1_7/extensions/regress-368224.js
+++ b/js/src/tests/js1_7/extensions/regress-368224.js
@@ -15,14 +15,14 @@ test();
 //-----------------------------------------------------------------------------
 
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
-  ({ x: [] }) = {}
+  ({ x: a }) = {}
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_8/regress/regress-469625-03.js
+++ b/js/src/tests/js1_8/regress/regress-469625-03.js
@@ -21,17 +21,17 @@ function test()
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   function f(x) {
     var [a, b, [c0, c1]] = [x, x, x];
   }
 
-  expect = 'TypeError: (intermediate value)[2] is null';
+  expect = 'TypeError: (intermediate value)[\'@@iterator\'](...).next(...).value is null';
   actual = 'No Error';
   try
   {
     f(null);
   }
   catch(ex)
   {
     actual = ex + '';
--- a/js/src/tests/js1_8_1/regress/regress-452498-052.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-052.js
@@ -22,17 +22,17 @@ function test()
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
 // ------- Comment #52 From Jason Orendorff
 
 // Crash in NoteLValue, called from BindDestructuringVar.
 // NoteLValue assumes pn->pn_lexdef is non-null, but here
 // pn is itself the definition of x.
-  for (var [x]=0 in null) ;
+  for (var [x]=[] in null) ;
 
 // This one only crashes when executed from a file.
 // Assertion failure: pn != dn->dn_uses, at ../jsparse.cpp:1131
   for (var f in null)
     ;
   var f = 1;
   (f)
 
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -645,16 +645,19 @@ function testParamPatternCombinations(ma
 testParamPatternCombinations(function(n) ("{a" + n + ":x" + n + "," + "b" + n + ":y" + n + "," + "c" + n + ":z" + n + "}"),
                              function(n) (objPatt([{ key: ident("a" + n), value: ident("x" + n) },
                                                    { key: ident("b" + n), value: ident("y" + n) },
                                                    { key: ident("c" + n), value: ident("z" + n) }])));
 
 testParamPatternCombinations(function(n) ("[x" + n + "," + "y" + n + "," + "z" + n + "]"),
                              function(n) (arrPatt([ident("x" + n), ident("y" + n), ident("z" + n)])));
 
+testParamPatternCombinations(function(n) ("[a" + n + ", ..." + "b" + n + "]"),
+                             function(n) (arrPatt([ident("a" + n), spread(ident("b" + n))])));
+
 
 // destructuring variable declarations
 
 function testVarPatternCombinations(makePattSrc, makePattPatt) {
     var pattSrcs = makePatternCombinations(function(n) ("x" + n), makePattSrc);
     var pattPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: null }), makePattPatt);
 
     for (var i = 0; i < pattSrcs.length; i++) {
@@ -682,16 +685,20 @@ testVarPatternCombinations(function (n) 
                                                         { key: ident("b" + n), value: ident("y" + n) },
                                                         { key: ident("c" + n), value: ident("z" + n) }]),
                                            init: lit(0) }));
 
 testVarPatternCombinations(function(n) ("[x" + n + "," + "y" + n + "," + "z" + n + "] = 0"),
                            function(n) ({ id: arrPatt([ident("x" + n), ident("y" + n), ident("z" + n)]),
                                           init: lit(0) }));
 
+testVarPatternCombinations(function(n) ("[a" + n + ", ..." + "b" + n + "] = 0"),
+                           function(n) ({ id: arrPatt([ident("a" + n), spread(ident("b" + n))]),
+                                          init: lit(0) }));
+
 // destructuring assignment
 
 function testAssignmentCombinations(makePattSrc, makePattPatt) {
     var pattSrcs = makePatternCombinations(function(n) ("x" + n + " = 0"), makePattSrc);
     var pattPatts = makePatternCombinations(function(n) (aExpr("=", ident("x" + n), lit(0))), makePattPatt);
 
     for (var i = 0; i < pattSrcs.length; i++) {
         var src = pattSrcs[i].join(",");
--- a/js/src/tests/js1_8_5/regress/regress-609617.js
+++ b/js/src/tests/js1_8_5/regress/regress-609617.js
@@ -28,36 +28,39 @@ try {
 var lhs_prefix = ["",        "++", "--", "",   "",   "[",             "[y, "      ];
 var lhs_suffix = [" = 'no'", "",   "",   "++", "--", ", y] = [3, 4]", "] = [5, 6]"];
 
 for (var i = 0; i < lhs_prefix.length; i++) {
     try {
         eval(lhs_prefix[i] + "eval('x')" + lhs_suffix[i]);
         assertEq(i, -2);
     } catch (e) {
-        /*
-         * NB: JSOP_SETCALL throws only JSMSG_BAD_LEFTSIDE_OF_ASS, it does not
-         * specialize for ++ and -- as the compiler's error reporting does. See
-         * the next section's forked assertEq code.
-         */
-        assertEq(e.message, "invalid assignment left-hand side");
+        if (/\[/.test(lhs_prefix[i])) {
+            assertEq(e.message, "invalid destructuring target");
+        } else {
+            /*
+             * NB: JSOP_SETCALL throws only JSMSG_BAD_LEFTSIDE_OF_ASS, it does not
+             * specialize for ++ and -- as the compiler's error reporting does. See
+             * the next section's forked assertEq code.
+             */
+            assertEq(e.message, "invalid assignment left-hand side");
+        }
     }
 }
 
-/* Destructuring desugars in the obvious way, so y must be 5 here. */
-assertEq(y, 5);
-
 /* Now test for strict mode rejecting any SETCALL variant at compile time. */
 for (var i = 0; i < lhs_prefix.length; i++) {
     try {
         eval("(function () { 'use strict'; " + lhs_prefix[i] + "foo('x')" + lhs_suffix[i] + "; })");
         assertEq(i, -3);
     } catch (e) {
         if (/\+\+|\-\-/.test(lhs_prefix[i] || lhs_suffix[i]))
             assertEq(e.message, "invalid increment/decrement operand");
+        else if (/\[/.test(lhs_prefix[i]))
+            assertEq(e.message, "invalid destructuring target");
         else
             assertEq(e.message, "invalid assignment left-hand side");
     }
 }
 
 /*
  * The useless delete is optimized away, but the SETCALL must not be. It's not
  * an early error, though.
--- a/js/src/tests/js1_8_5/regress/regress-646820-3.js
+++ b/js/src/tests/js1_8_5/regress/regress-646820-3.js
@@ -1,9 +1,9 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 (function () {
-    var [x, y] = {"0": function () y, "1": 13};
+    var [x, y] = [function () y, 13];
     assertEq(x(), 13);
 })();
 
 reportCompare(0, 0, 'ok');