Bug 1204028 - Evaluate LHS reference before RHS in destructuring. r=shu
authorTooru Fujisawa <arai_a@mac.com>
Wed, 21 Dec 2016 17:40:49 +0900
changeset 326779 126cbbb0afcd6123fa538cad01ba4eec9660e455
parent 326778 9084fa68537466911b5aabf7deaba236bbc5932f
child 326780 1b5b00a0af64d2ed1bf6f502e1408acd1175456a
push id31111
push usercbook@mozilla.com
push dateWed, 21 Dec 2016 15:57:24 +0000
treeherdermozilla-central@10a5e2fc24e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1204028
milestone53.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 1204028 - Evaluate LHS reference before RHS in destructuring. r=shu
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/IonBuilder.cpp
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
js/src/tests/ecma_6/Destructuring/order-super.js
js/src/tests/ecma_6/Destructuring/order.js
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4322,17 +4322,79 @@ BytecodeEmitter::emitDestructuringDeclsW
             if (!emitDestructuringDeclsWithEmitter(target, emitName))
                 return false;
         }
     }
     return true;
 }
 
 bool
-BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav)
+BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted)
+{
+    *emitted = 0;
+
+    if (target->isKind(PNK_SPREAD))
+        target = target->pn_kid;
+    else if (target->isKind(PNK_ASSIGN))
+        target = target->pn_left;
+
+    // No need to recur into PNK_ARRAY and PNK_OBJECT subpatterns here, since
+    // emitSetOrInitializeDestructuring does the recursion when setting or
+    // initializing value.  Getting reference doesn't recur.
+    if (target->isKind(PNK_NAME) || target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT))
+        return true;
+
+#ifdef DEBUG
+    int depth = stackDepth;
+#endif
+
+    switch (target->getKind()) {
+      case PNK_DOT: {
+        if (target->as<PropertyAccess>().isSuper()) {
+            if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
+                return false;
+            *emitted = 2;
+        } else {
+            if (!emitTree(target->pn_expr))
+                return false;
+            *emitted = 1;
+        }
+        break;
+      }
+
+      case PNK_ELEM: {
+        if (target->as<PropertyByValue>().isSuper()) {
+            if (!emitSuperElemOperands(target, EmitElemOption::Ref))
+                return false;
+            *emitted = 3;
+        } else {
+            if (!emitElemOperands(target, EmitElemOption::Ref))
+                return false;
+            *emitted = 2;
+        }
+        break;
+      }
+
+      case PNK_CALL:
+        MOZ_ASSERT_UNREACHABLE("Parser::reportIfNotValidSimpleAssignmentTarget "
+                               "rejects function calls as assignment "
+                               "targets in destructuring assignments");
+        break;
+
+      default:
+        MOZ_CRASH("emitDestructuringLHSRef: bad lhs kind");
+    }
+
+    MOZ_ASSERT(stackDepth == depth + int(*emitted));
+
+    return true;
+}
+
+bool
+BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav)
 {
     // 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 (target->isKind(PNK_SPREAD))
         target = target->pn_kid;
     else if (target->isKind(PNK_ASSIGN))
@@ -4398,67 +4460,51 @@ BytecodeEmitter::emitDestructuringLHS(Pa
                     return false;
                 break;
             }
 
             break;
           }
 
           case PNK_DOT: {
-            // See the (PNK_NAME, JSOP_SETNAME) case above.
-            //
-            // In `a.x = b`, `a` is evaluated first, then `b`, then a
-            // JSOP_SETPROP instruction.
-            //
-            // In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we
-            // need a property set -- but the operands are on the stack in the
-            // wrong order for JSOP_SETPROP, so we have to add a JSOP_SWAP.
+            // The reference is already pushed by emitDestructuringLHSRef.
             JSOp setOp;
-            if (target->as<PropertyAccess>().isSuper()) {
-                if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
-                    return false;
-                if (!emit2(JSOP_PICK, 2))
-                    return false;
+            if (target->as<PropertyAccess>().isSuper())
                 setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
-            } else {
-                if (!emitTree(target->pn_expr))
-                    return false;
-                if (!emit1(JSOP_SWAP))
-                    return false;
+            else
                 setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
-            }
             if (!emitAtomOp(target, setOp))
                 return false;
             break;
           }
 
           case PNK_ELEM: {
-            // See the comment at `case PNK_DOT:` above. This case,
-            // `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP
-            // is emitted by emitElemOperands.
+            // The reference is already pushed by emitDestructuringLHSRef.
             if (target->as<PropertyByValue>().isSuper()) {
                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
-                if (!emitSuperElemOp(target, setOp))
+                // emitDestructuringLHSRef already did emitSuperElemOperands
+                // part of emitSuperElemOp.  Perform remaining part here.
+                if (!emitElemOpBase(setOp))
                     return false;
             } else {
                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
-                if (!emitElemOp(target, setOp))
+                if (!emitElemOpBase(setOp))
                     return false;
             }
             break;
           }
 
           case PNK_CALL:
             MOZ_ASSERT_UNREACHABLE("Parser::reportIfNotValidSimpleAssignmentTarget "
                                    "rejects function calls as assignment "
                                    "targets in destructuring assignments");
             break;
 
           default:
-            MOZ_CRASH("emitDestructuringLHS: bad lhs kind");
+            MOZ_CRASH("emitSetOrInitializeDestructuring: bad lhs kind");
         }
 
         // Pop the assigned value.
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
@@ -4729,128 +4775,145 @@ BytecodeEmitter::emitDestructuringOpsArr
     MOZ_ASSERT(pattern->isKind(PNK_ARRAY));
     MOZ_ASSERT(pattern->isArity(PN_LIST));
     MOZ_ASSERT(this->stackDepth != 0);
 
     // Here's pseudo code for |let [a, b, , c=y, ...d] = x;|
     //
     //   let x, y;
     //   let a, b, c, d;
-    //   let iter, result, done, value; // stack values
+    //   let iter, lref, result, done, value; // stack values
     //
     //   iter = x[Symbol.iterator]();
     //
     //   // ==== emitted by loop for a ====
+    //   lref = GetReference(a);
+    //
     //   result = iter.next();
     //   done = result.done;
     //
     //   if (done)
     //     value = undefined;
     //   else
     //     value = result.value;
     //
-    //   a = value;
+    //   SetOrInitialize(lref, value);
     //
     //   // ==== emitted by loop for b ====
+    //   lref = GetReference(b);
+    //
     //   if (done) {
     //     value = undefined;
     //   } else {
     //     result = iter.next();
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
-    //   b = value;
+    //   SetOrInitialize(lref, value);
     //
     //   // ==== emitted by loop for elision ====
     //   if (done) {
     //     value = undefined;
     //   } else {
     //     result = iter.next();
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
     //   // ==== emitted by loop for c ====
+    //   lref = GetReference(c);
+    //
     //   if (done) {
     //     value = undefined;
     //   } else {
     //     result = iter.next();
     //     done = result.done;
     //     if (done)
     //       value = undefined;
     //     else
     //       value = result.value;
     //   }
     //
     //   if (value === undefined)
     //     value = y;
     //
-    //   c = value;
+    //   SetOrInitialize(lref, value);
     //
     //   // ==== emitted by loop for d ====
+    //   lref = GetReference(d);
+    //
     //   if (done)
     //     value = [];
     //   else
     //     value = [...iter];
     //
-    //   d = value;
+    //   SetOrInitialize(lref, value);
 
     // Use an iterator to destructure the RHS, instead of index lookup. We
     // must leave the *original* value on the stack.
     if (!emit1(JSOP_DUP))                                         // ... OBJ OBJ
         return false;
     if (!emitIterator())                                          // ... OBJ ITER
         return false;
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
         bool isHead = member == pattern->pn_head;
         bool hasNext = !!member->pn_next;
 
         if (member->isKind(PNK_SPREAD)) {
+            size_t emitted = 0;
+            if (!emitDestructuringLHSRef(member, &emitted))       // ... OBJ ITER ?DONE *LREF
+                return false;
+
             IfThenElseEmitter ifThenElse(this);
             if (!isHead) {
                 // If spread is not the first element of the pattern,
                 // iterator can already be completed.
-                //                                                   ... OBJ ITER DONE
-                if (!ifThenElse.emitIfElse())                     // ... OBJ ITER
+                //                                                   ... OBJ ITER DONE *LREF
+                if (emitted) {
+                    if (!emit2(JSOP_PICK, emitted))               // ... OBJ ITER *LREF DONE
+                        return false;
+                }
+
+                if (!ifThenElse.emitIfElse())                     // ... OBJ ITER *LREF
                     return false;
 
-                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ ITER ARRAY
+                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ ITER *LREF ARRAY
                     return false;
-                if (!ifThenElse.emitElse())                       // ... OBJ ITER
+                if (!ifThenElse.emitElse())                       // ... OBJ ITER *LREF
                     return false;
             }
 
             // If iterator is not completed, create a new array with the rest
             // of the iterator.
-            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER
-                return false;
-            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER ITER ARRAY
-                return false;
-            if (!emitNumberOp(0))                                 // ... OBJ ITER ITER ARRAY INDEX
-                return false;
-            if (!emitSpread())                                    // ... OBJ ITER ARRAY INDEX
-                return false;
-            if (!emit1(JSOP_POP))                                 // ... OBJ ITER ARRAY
+            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
+                return false;
+            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER *LREF ITER ARRAY
+                return false;
+            if (!emitNumberOp(0))                                 // ... OBJ ITER *LREF ITER ARRAY INDEX
+                return false;
+            if (!emitSpread())                                    // ... OBJ ITER *LREF ARRAY INDEX
+                return false;
+            if (!emit1(JSOP_POP))                                 // ... OBJ ITER *LREF ARRAY
                 return false;
 
             if (!isHead) {
                 if (!ifThenElse.emitEnd())
                     return false;
                 MOZ_ASSERT(ifThenElse.pushed() == 1);
             }
 
-            if (!emitDestructuringLHS(member, flav))              // ... OBJ ITER
+            if (!emitSetOrInitializeDestructuring(member, flav))  // ... OBJ ITER
                 return false;
 
             MOZ_ASSERT(!hasNext);
             break;
         }
 
         ParseNode* pndefault = nullptr;
         ParseNode* subpattern = member;
@@ -4858,99 +4921,124 @@ BytecodeEmitter::emitDestructuringOpsArr
             pndefault = subpattern->pn_right;
             subpattern = subpattern->pn_left;
         }
 
         bool isElision = subpattern->isKind(PNK_ELISION);
 
         MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD));
 
+        size_t emitted = 0;
+        if (!isElision) {
+            if (!emitDestructuringLHSRef(subpattern, &emitted))   // ... OBJ ITER ?DONE *LREF
+                return false;
+        }
+
         IfThenElseEmitter ifAlreadyDone(this);
         if (!isHead) {
             // If this element is not the first element of the pattern,
             // iterator can already be completed.
-            //                                                       ... OBJ ITER DONE
-            if (hasNext) {
-                if (!emit1(JSOP_DUP))                             // ... OBJ ITER DONE DONE
-                    return false;
+            //                                                       ... OBJ ITER DONE *LREF
+            if (emitted) {
+                if (hasNext) {
+                    if (!emitDupAt(emitted))                      // ... OBJ ITER DONE *LREF DONE
+                        return false;
+                } else {
+                    if (!emit2(JSOP_PICK, emitted))               // ... OBJ ITER *LREF DONE
+                        return false;
+                }
+            } else {
+                if (hasNext) {
+                    // The position of LREF in the following stack comment
+                    // isn't accurate for the operation, but it's equivalent
+                    // since LREF is nothing
+                    if (!emit1(JSOP_DUP))                         // ... OBJ ITER DONE *LREF DONE
+                        return false;
+                }
             }
-            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER ?DONE
-                return false;
-
-            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER ?DONE UNDEF
-                return false;
-            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER ?DONE UNDEF
-                return false;
-
-            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER ?DONE
+            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER ?DONE *LREF
+                return false;
+
+            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER ?DONE *LREF UNDEF
+                return false;
+            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER ?DONE *LREF UNDEF
+                return false;
+
+            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER ?DONE *LREF
                 return false;
 
             if (hasNext) {
-                if (!emit1(JSOP_POP))                             // ... OBJ ITER
+                if (emitted) {
+                    if (!emit2(JSOP_PICK, emitted))               // ... OBJ ITER *LREF DONE
+                        return false;
+                }
+                if (!emit1(JSOP_POP))                             // ... OBJ ITER *LREF
                     return false;
             }
         }
 
-        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER ITER
-            return false;
-        if (!emitIteratorNext(pattern))                           // ... OBJ ITER RESULT
-            return false;
-        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER RESULT DONE
+        if (emitted) {
+            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
+                return false;
+        } else {
+            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER *LREF ITER
+                return false;
+        }
+        if (!emitIteratorNext(pattern))                           // ... OBJ ITER *LREF RESULT
+            return false;
+        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER *LREF RESULT DONE
             return false;
 
         if (hasNext) {
-            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER RESULT DONE DONE
+            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER *LREF RESULT DONE DONE
+                return false;
+            if (!emit2(JSOP_UNPICK, emitted + 2))                 // ... OBJ ITER DONE *LREF RESULT DONE
                 return false;
         }
 
         IfThenElseEmitter ifDone(this);
-        if (!ifDone.emitIfElse())                                 // ... OBJ ITER RESULT ?DONE
-            return false;
-
-        if (hasNext) {
-            if (!emit1(JSOP_SWAP))                                // ... OBJ ITER ?DONE RESULT
-                return false;
-        }
-        if (!emit1(JSOP_POP))                                     // ... OBJ ITER ?DONE
-            return false;
-        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER ?DONE UNDEF
-            return false;
-        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER ?DONE UNDEF
-            return false;
-
-        if (!ifDone.emitElse())                                   // ... OBJ ITER RESULT ?DONE
-            return false;
-
-        if (hasNext) {
-            if (!emit1(JSOP_SWAP))                                // ... OBJ ITER ?DONE RESULT
-                return false;
-        }
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ ITER ?DONE VALUE
+        if (!ifDone.emitIfElse())                                 // ... OBJ ITER ?DONE *LREF RESULT
+            return false;
+
+        if (!emit1(JSOP_POP))                                     // ... OBJ ITER ?DONE *LREF
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER ?DONE *LREF UNDEF
+            return false;
+        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER ?DONE *LREF UNDEF
+            return false;
+
+        if (!ifDone.emitElse())                                   // ... OBJ ITER ?DONE *LREF RESULT
+            return false;
+
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ ITER ?DONE *LREF VALUE
             return false;
 
         if (!ifDone.emitEnd())
             return false;
         MOZ_ASSERT(ifDone.pushed() == 0);
 
         if (!isHead) {
             if (!ifAlreadyDone.emitEnd())
                 return false;
             MOZ_ASSERT(ifAlreadyDone.pushed() == 1);
         }
 
         if (pndefault) {
-            if (!emitDefault(pndefault, subpattern))              // ... OBJ ITER ?DONE VALUE
+            if (!emitDefault(pndefault, subpattern))              // ... OBJ ITER ?DONE *LREF VALUE
                 return false;
         }
 
         if (!isElision) {
-            if (!emitDestructuringLHS(subpattern, flav))          // ... OBJ ITER ?DONE
-                return false;
+            if (!emitSetOrInitializeDestructuring(subpattern,
+                                                  flav))          // ... OBJ ITER ?DONE
+            {
+                return false;
+            }
         } else {
             if (!emit1(JSOP_POP))                                 // ... OBJ ITER ?DONE
                 return false;
         }
     }
 
     if (!emit1(JSOP_POP))                                         // ... OBJ
         return false;
@@ -4972,73 +5060,86 @@ BytecodeEmitter::emitDestructuringOpsObj
     MOZ_ASSERT(pattern->isArity(PN_LIST));
 
     MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS
 
     if (!emitRequireObjectCoercible())                            // ... RHS
         return false;
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
+        ParseNode* subpattern;
+        if (member->isKind(PNK_MUTATEPROTO))
+            subpattern = member->pn_kid;
+        else
+            subpattern = member->pn_right;
+        ParseNode* lhs = subpattern;
+        if (lhs->isKind(PNK_ASSIGN))
+            lhs = lhs->pn_left;
+
+        size_t emitted;
+        if (!emitDestructuringLHSRef(lhs, &emitted))              // ... RHS *LREF
+            return false;
+
         // Duplicate the value being destructured to use as a reference base.
-        if (!emit1(JSOP_DUP))                                     // ... RHS RHS
-            return false;
+        if (emitted) {
+            if (!emitDupAt(emitted))                              // ... RHS *LREF RHS
+                return false;
+        } else {
+            if (!emit1(JSOP_DUP))                                 // ... RHS RHS
+                return false;
+        }
 
         // 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.
         bool needsGetElem = true;
 
-        ParseNode* subpattern;
         if (member->isKind(PNK_MUTATEPROTO)) {
-            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... RHS PROP
+            if (!emitAtomOp(cx->names().proto, JSOP_GETPROP))     // ... RHS *LREF PROP
                 return false;
             needsGetElem = false;
-            subpattern = member->pn_kid;
         } else {
             MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
 
             ParseNode* key = member->pn_left;
             if (key->isKind(PNK_NUMBER)) {
-                if (!emitNumberOp(key->pn_dval))                  // ... RHS RHS KEY
+                if (!emitNumberOp(key->pn_dval))                  // ... RHS *LREF RHS KEY
                     return false;
             } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
                 PropertyName* name = key->pn_atom->asPropertyName();
 
                 // The parser already checked for atoms representing indexes and
                 // used PNK_NUMBER instead, but also watch for ids which TI treats
                 // as indexes for simplification of downstream analysis.
                 jsid id = NameToId(name);
                 if (id != IdToTypeId(id)) {
-                    if (!emitTree(key))                           // ... RHS RHS KEY
+                    if (!emitTree(key))                           // ... RHS *LREF RHS KEY
                         return false;
                 } else {
-                    if (!emitAtomOp(name, JSOP_GETPROP))          // ...RHS PROP
+                    if (!emitAtomOp(name, JSOP_GETPROP))          // ... RHS *LREF PROP
                         return false;
                     needsGetElem = false;
                 }
             } else {
-                if (!emitComputedPropertyName(key))               // ... RHS RHS KEY
+                if (!emitComputedPropertyName(key))               // ... RHS *LREF RHS KEY
                     return false;
             }
-
-            subpattern = member->pn_right;
         }
 
         // Get the property value if not done already.
-        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... RHS PROP
+        if (needsGetElem && !emitElemOpBase(JSOP_GETELEM))        // ... RHS *LREF PROP
             return false;
 
         if (subpattern->isKind(PNK_ASSIGN)) {
-            if (!emitDefault(subpattern->pn_right, subpattern->pn_left))
-                return false;
-            subpattern = subpattern->pn_left;
-        }
-
-        // Destructure PROP per this member's subpattern.
-        if (!emitDestructuringLHS(subpattern, flav))
+            if (!emitDefault(subpattern->pn_right, lhs))          // ... RHS *LREF VALUE
+                return false;
+        }
+
+        // Destructure PROP per this member's lhs.
+        if (!emitSetOrInitializeDestructuring(subpattern, flav))  // ... RHS
             return false;
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav)
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -604,17 +604,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
     MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow);
 
     MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
 
     // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
     // opcode onto the stack in the right order. In the case of SETELEM, the
     // value to be assigned must already be pushed.
-    enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign };
+    enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref };
     MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
 
     MOZ_MUST_USE bool emitElemOpBase(JSOp op);
     MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op);
     MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
 
     MOZ_MUST_USE bool emitCatch(ParseNode* pn);
     MOZ_MUST_USE bool emitIf(ParseNode* pn);
@@ -637,20 +637,28 @@ struct MOZ_STACK_CLASS BytecodeEmitter
         // contains the function body's vars, but into its enclosing scope for
         // parameter expressions.
         DestructuringFormalParameterInVarScope,
 
         // Destructuring as part of an AssignmentExpression.
         DestructuringAssignment
     };
 
-    // emitDestructuringLHS assumes the to-be-destructured value has been pushed on
-    // the stack and emits code to destructure a single lhs expression (either a
-    // name or a compound []/{} expression).
-    MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav);
+    // emitDestructuringLHSRef emits the lhs expression's reference.
+    // If the lhs expression is object property |OBJ.prop|, it emits |OBJ|.
+    // If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|.
+    // If there's nothing to evaluate for the reference, it emits nothing.
+    // |emitted| parameter receives the number of values pushed onto the stack.
+    MOZ_MUST_USE bool emitDestructuringLHSRef(ParseNode* target, size_t* emitted);
+
+    // emitSetOrInitializeDestructuring assumes the lhs expression's reference
+    // and the to-be-destructured value has been pushed on the stack.  It emits
+    // code to destructure a single lhs expression (either a name or a compound
+    // []/{} expression).
+    MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
 
     // emitDestructuringOps assumes the to-be-destructured value has been
     // pushed on the stack and emits code to destructure each part of a [] or
     // {} lhs expression.
     MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
     MOZ_MUST_USE bool emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav);
     MOZ_MUST_USE bool emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav);
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1142,17 +1142,17 @@ BaselineCompiler::emit_JSOP_PICK()
     frame.syncStack(0);
 
     // Pick takes a value on the stack and moves it to the top.
     // For instance, pick 2:
     //     before: A B C D E
     //     after : A B D E C
 
     // First, move value at -(amount + 1) into R0.
-    int depth = -(GET_INT8(pc) + 1);
+    int32_t depth = -(GET_INT8(pc) + 1);
     masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
 
     // Move the other values down.
     depth++;
     for (; depth < 0; depth++) {
         Address source = frame.addressOfStackValue(frame.peek(depth));
         Address dest = frame.addressOfStackValue(frame.peek(depth - 1));
         masm.loadValue(source, R1);
@@ -1161,16 +1161,44 @@ BaselineCompiler::emit_JSOP_PICK()
 
     // Push R0.
     frame.pop();
     frame.push(R0);
     return true;
 }
 
 bool
+BaselineCompiler::emit_JSOP_UNPICK()
+{
+    frame.syncStack(0);
+
+    // Pick takes the top of the stack value and moves it under the nth value.
+    // For instance, unpick 2:
+    //     before: A B C D E
+    //     after : A B E C D
+
+    // First, move value at -1 into R0.
+    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+    // Move the other values up.
+    int32_t depth = -(GET_INT8(pc) + 1);
+    for (int32_t i = -1; i > depth; i--) {
+        Address source = frame.addressOfStackValue(frame.peek(i - 1));
+        Address dest = frame.addressOfStackValue(frame.peek(i));
+        masm.loadValue(source, R1);
+        masm.storeValue(R1, dest);
+    }
+
+    // Store R0 under the nth value.
+    Address dest = frame.addressOfStackValue(frame.peek(depth));
+    masm.storeValue(R0, dest);
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_GOTO()
 {
     frame.syncStack(0);
 
     jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
     masm.jump(labelOf(target));
     return true;
 }
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -37,16 +37,17 @@ namespace jit {
     _(JSOP_POPN)               \
     _(JSOP_DUPAT)              \
     _(JSOP_ENTERWITH)          \
     _(JSOP_LEAVEWITH)          \
     _(JSOP_DUP)                \
     _(JSOP_DUP2)               \
     _(JSOP_SWAP)               \
     _(JSOP_PICK)               \
+    _(JSOP_UNPICK)             \
     _(JSOP_GOTO)               \
     _(JSOP_IFEQ)               \
     _(JSOP_IFNE)               \
     _(JSOP_AND)                \
     _(JSOP_OR)                 \
     _(JSOP_NOT)                \
     _(JSOP_POS)                \
     _(JSOP_LOOPHEAD)           \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1552,16 +1552,17 @@ IonBuilder::visitBlock(const CFGBlock* c
         for (size_t i = 0; i < popped.length(); i++) {
             switch (op) {
               case JSOP_POP:
               case JSOP_POPN:
               case JSOP_DUPAT:
               case JSOP_DUP:
               case JSOP_DUP2:
               case JSOP_PICK:
+              case JSOP_UNPICK:
               case JSOP_SWAP:
               case JSOP_SETARG:
               case JSOP_SETLOCAL:
               case JSOP_INITLEXICAL:
               case JSOP_SETRVAL:
               case JSOP_VOID:
                 // Don't require SSA uses for values popped by these ops.
                 break;
@@ -2114,16 +2115,20 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_SWAP:
         current->swapAt(-1);
         return true;
 
       case JSOP_PICK:
         current->pick(-GET_INT8(pc));
         return true;
 
+      case JSOP_UNPICK:
+        current->unpick(-GET_INT8(pc));
+        return true;
+
       case JSOP_GETALIASEDVAR:
         return jsop_getaliasedvar(EnvironmentCoordinate(pc));
 
       case JSOP_SETALIASEDVAR:
         return jsop_setaliasedvar(EnvironmentCoordinate(pc));
 
       case JSOP_UINT24:
         pushConstant(Int32Value(GET_UINT24(pc)));
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -840,16 +840,29 @@ MBasicBlock::pick(int32_t depth)
     //   A B C D E
     //   A B D C E [ swapAt(-2) ]
     //   A B D E C [ swapAt(-1) ]
     for (; depth < 0; depth++)
         swapAt(depth);
 }
 
 void
+MBasicBlock::unpick(int32_t depth)
+{
+    // unpick take the top of the stack element and move it under the depth-th
+    // element;
+    // unpick(-2):
+    //   A B C D E
+    //   A B C E D [ swapAt(-1) ]
+    //   A B E C D [ swapAt(-2) ]
+    for (int32_t n = -1; n >= depth; n--)
+        swapAt(n);
+}
+
+void
 MBasicBlock::swapAt(int32_t depth)
 {
     uint32_t lhsDepth = stackPosition_ + depth - 1;
     uint32_t rhsDepth = stackPosition_ + depth;
 
     MDefinition* temp = slots_[lhsDepth];
     slots_[lhsDepth] = slots_[rhsDepth];
     slots_[rhsDepth] = temp;
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -140,16 +140,19 @@ class MBasicBlock : public TempObject, p
         unreachable_ = true;
     }
     bool unreachable() const {
         return unreachable_;
     }
     // Move the definition to the top of the stack.
     void pick(int32_t depth);
 
+    // Move the top of the stack definition under the depth-th stack value.
+    void unpick(int32_t depth);
+
     // Exchange 2 stack slots at the defined depth
     void swapAt(int32_t depth);
 
     // Gets the instruction associated with various slot types.
     MDefinition* peek(int32_t depth);
 
     MDefinition* environmentChain();
     MDefinition* argumentsObject();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/order-super.js
@@ -0,0 +1,727 @@
+var BUGNUMBER = 1204028;
+var summary = "Destructuring should evaluate lhs reference before rhs in super property";
+
+if (typeof assertEq === "undefined") {
+  assertEq = function(a, b) {
+    if (a !== b)
+      throw new Error(`expected ${b}, got ${a}\n${new Error().stack}`);
+  };
+}
+
+print(BUGNUMBER + ": " + summary);
+
+let logs = [];
+function log(x) {
+  logs.push(x);
+}
+
+let unwrapMap = new Map();
+function unwrap(maybeWrapped) {
+  if (unwrapMap.has(maybeWrapped))
+    return unwrapMap.get(maybeWrapped);
+  return maybeWrapped;
+}
+function ToString(name) {
+  if (name == Symbol.iterator)
+    return "@@iterator";
+  return String(name);
+}
+function logger(obj, prefix=[]) {
+  let wrapped = new Proxy(obj, {
+    get(that, name) {
+      if (name == "return") {
+        // FIXME: Bug 1147371.
+        // We ignore IteratorClose for now.
+        return obj[name];
+      }
+
+      let names = prefix.concat(ToString(name));
+      log("rhs get " + names.join("::"));
+      let v = obj[name];
+      if (typeof v === "object" || typeof v === "function")
+        return logger(v, names);
+      return v;
+    },
+    apply(that, thisArg, args) {
+      let names = prefix.slice();
+      log("rhs call " + names.join("::"));
+      let v = obj.apply(unwrap(thisArg), args);
+      if (typeof v === "object" || typeof v === "function") {
+        names[names.length - 1] += "()";
+        return logger(v, names);
+      }
+      return v;
+    }
+  });
+  unwrapMap.set(wrapped, obj);
+  return wrapped;
+}
+
+class C1 {
+  constructor() {
+    this.clear();
+  }
+  clear() {
+    this.values = {};
+  }
+}
+for (let name of [
+  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+  "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+  "0", "1", "length"
+]) {
+  Object.defineProperty(C1.prototype, name, {
+    set: function(value) {
+      log("lhs set " + name);
+      this.values[name] = value;
+    }
+  });
+}
+class C2 extends C1 {
+  constructor() {
+    super();
+
+    let clear = () => {
+      logs = [];
+      this.clear();
+    };
+
+    // Array.
+
+    clear();
+    [
+      super.a
+    ] = logger(["A"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a, "A");
+
+    clear();
+    [
+      super[ (log("lhs before name a"), "a") ]
+    ] = logger(["A"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "lhs before name a",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a, "A");
+
+    // Array rest.
+
+    clear();
+    [
+      ...super.a
+    ] = logger(["A", "B", "C"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a.join(","), "A,B,C");
+
+    clear();
+    [
+      ...super[ (log("lhs before name a"), "a") ]
+    ] = logger(["A", "B", "C"]);;
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "lhs before name a",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a.join(","), "A,B,C");
+
+    // Array combined.
+
+    clear();
+    [
+      super.a,
+      super[ (log("lhs before name b"), "b") ],
+      ...super[ (log("lhs before name c"), "c") ]
+    ] = logger(["A", "B", "C"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "lhs set a",
+
+               "lhs before name b",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "lhs set b",
+
+               "lhs before name c",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "lhs set c",
+             ].join(","));
+    assertEq(this.values.a, "A");
+    assertEq(this.values.b, "B");
+    assertEq(this.values.c.join(","), "C");
+
+    // Object.
+
+    clear();
+    ({
+      a: super.a
+    } = logger({a: "A"}));
+    assertEq(logs.join(","),
+             [
+               "rhs get a",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a, "A");
+
+    clear();
+    ({
+      a: super[ (log("lhs before name a"), "a") ]
+    } = logger({a: "A"}));
+    assertEq(logs.join(","),
+             [
+               "lhs before name a",
+               "rhs get a",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a, "A");
+
+    // Object combined.
+
+    clear();
+    ({
+      a: super.a,
+      b: super[ (log("lhs before name b"), "b") ]
+    } = logger({a: "A", b: "B"}));
+    assertEq(logs.join(","),
+             [
+               "rhs get a",
+               "lhs set a",
+
+               "lhs before name b",
+               "rhs get b",
+               "lhs set b",
+             ].join(","));
+    assertEq(this.values.a, "A");
+    assertEq(this.values.b, "B");
+
+    // == Nested ==
+
+    // Array -> Array
+
+    clear();
+    [
+      [
+        super[ (log("lhs before name a"), "a") ],
+        ...super[ (log("lhs before name b"), "b") ]
+      ]
+    ] = logger([["A", "B"]]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next()::value::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator",
+
+               "lhs before name a",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+               "lhs set a",
+
+               "lhs before name b",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "lhs set b",
+             ].join(","));
+    assertEq(this.values.a, "A");
+    assertEq(this.values.b.length, 1);
+    assertEq(this.values.b[0], "B");
+
+    // Array rest -> Array
+
+    clear();
+    [
+      ...[
+        super[ (log("lhs before name a"), "a") ],
+        ...super[ (log("lhs before name b"), "b") ]
+      ]
+    ] = logger(["A", "B"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+
+               "lhs before name a",
+               "lhs set a",
+
+               "lhs before name b",
+               "lhs set b",
+             ].join(","));
+    assertEq(this.values.a, "A");
+    assertEq(this.values.b.join(","), "B");
+
+    // Array -> Object
+    clear();
+    [
+      {
+        a: super[ (log("lhs before name a"), "a") ]
+      }
+    ] = logger([{a: "A"}]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+
+               "lhs before name a",
+               "rhs get @@iterator()::next()::value::a",
+               "lhs set a",
+             ].join(","));
+    assertEq(this.values.a, "A");
+
+    // Array rest -> Object
+    clear();
+    [
+      ...{
+        0: super[ (log("lhs before name 0"), "0") ],
+        1: super[ (log("lhs before name 1"), "1") ],
+        length: super[ (log("lhs before name length"), "length") ],
+      }
+    ] = logger(["A", "B"]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+
+               "lhs before name 0",
+               "lhs set 0",
+
+               "lhs before name 1",
+               "lhs set 1",
+
+               "lhs before name length",
+               "lhs set length",
+             ].join(","));
+    assertEq(this.values["0"], "A");
+    assertEq(this.values["1"], "B");
+    assertEq(this.values.length, 2);
+
+    // Object -> Array
+    clear();
+    ({
+      a: [
+        super[ (log("lhs before name b"), "b") ]
+      ]
+    } = logger({a: ["B"]}));
+    assertEq(logs.join(","),
+             [
+               "rhs get a",
+               "rhs get a::@@iterator",
+               "rhs call a::@@iterator",
+
+               "lhs before name b",
+               "rhs get a::@@iterator()::next",
+               "rhs call a::@@iterator()::next",
+               "rhs get a::@@iterator()::next()::done",
+               "rhs get a::@@iterator()::next()::value",
+               "lhs set b",
+             ].join(","));
+    assertEq(this.values.b, "B");
+
+    // Object -> Object
+    clear();
+    ({
+      a: {
+        b: super[ (log("lhs before name b"), "b") ]
+      }
+    } = logger({a: {b: "B"}}));
+    assertEq(logs.join(","),
+             [
+               "rhs get a",
+               "lhs before name b",
+               "rhs get a::b",
+               "lhs set b",
+             ].join(","));
+    assertEq(this.values.b, "B");
+
+    // All combined
+
+    clear();
+    [
+      super[ (log("lhs before name a"), "a") ],
+      [
+        super[ (log("lhs before name b"), "b") ],
+        {
+          c: super[ (log("lhs before name c"), "c") ],
+          d: {
+            e: super[ (log("lhs before name e"), "e") ],
+            f: [
+              super[ (log("lhs before name g"), "g") ]
+            ]
+          }
+        }
+      ],
+      {
+        h: super[ (log("lhs before name h"), "h") ],
+        i: [
+          super[ (log("lhs before name j"), "j") ],
+          {
+            k: [
+              super[ (log("lhs before name l"), "l") ]
+            ]
+          }
+        ]
+      },
+      ...[
+        super[ (log("lhs before name m"), "m") ],
+        [
+          super[ (log("lhs before name n"), "n") ],
+          {
+            o: super[ (log("lhs before name o"), "o") ],
+            p: {
+              q: super[ (log("lhs before name q"), "q") ],
+              r: [
+                super[ (log("lhs before name s"), "s") ]
+              ]
+            }
+          }
+        ],
+        ...{
+          0: super[ (log("lhs before name t"), "t") ],
+          1: [
+            super[ (log("lhs before name u"), "u") ],
+            {
+              v: super[ (log("lhs before name v"), "v") ],
+              w: {
+                x: super[ (log("lhs before name x"), "x") ],
+                y: [
+                  super[ (log("lhs before name z"), "z") ]
+                ]
+              }
+            }
+          ],
+          length: super[ (log("lhs before name length"), "length") ],
+        }
+      ]
+    ] = logger(["A",
+                ["B", {c: "C", d: {e: "E", f: ["G"]}}],
+                {h: "H", i: ["J", {k: ["L"]}]},
+                "M",
+                ["N", {o: "O", p: {q: "Q", r: ["S"]}}],
+                "T", ["U", {v: "V", w: {x: "X", y: ["Z"]}}]]);
+    assertEq(logs.join(","),
+             [
+               "rhs get @@iterator",
+               "rhs call @@iterator",
+
+               "lhs before name a",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "lhs set a",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next()::value::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator",
+
+               "lhs before name b",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+               "lhs set b",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+               "lhs before name c",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::c",
+               "lhs set c",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d",
+
+               "lhs before name e",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::e",
+               "lhs set e",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator",
+
+               "lhs before name g",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::value",
+               "lhs set g",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+
+               "lhs before name h",
+               "rhs get @@iterator()::next()::value::h",
+               "lhs set h",
+
+               "rhs get @@iterator()::next()::value::i",
+               "rhs get @@iterator()::next()::value::i::@@iterator",
+               "rhs call @@iterator()::next()::value::i::@@iterator",
+
+               "lhs before name j",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::i::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value",
+               "lhs set j",
+
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::i::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value",
+
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator",
+               "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator",
+
+               "lhs before name l",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::value",
+               "lhs set l",
+
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value",
+               "rhs get @@iterator()::next",
+               "rhs call @@iterator()::next",
+               "rhs get @@iterator()::next()::done",
+
+               "lhs before name m",
+               "lhs set m",
+
+               "rhs get @@iterator()::next()::value::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator",
+
+               "lhs before name n",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+               "lhs set n",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+               "lhs before name o",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::o",
+               "lhs set o",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p",
+
+               "lhs before name q",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::q",
+               "lhs set q",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator",
+
+               "lhs before name s",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::value",
+               "lhs set s",
+
+               "lhs before name t",
+               "lhs set t",
+
+               "rhs get @@iterator()::next()::value::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator",
+
+               "lhs before name u",
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+               "lhs set u",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+               "lhs before name v",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::v",
+               "lhs set v",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w",
+
+               "lhs before name x",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::x",
+               "lhs set x",
+
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator",
+
+               "lhs before name z",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next",
+               "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::done",
+               "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::value",
+               "lhs set z",
+
+               "lhs before name length",
+               "lhs set length",
+             ].join(","));
+    assertEq(this.values.a, "A");
+    assertEq(this.values.b, "B");
+    assertEq(this.values.c, "C");
+    assertEq(this.values.e, "E");
+    assertEq(this.values.g, "G");
+    assertEq(this.values.h, "H");
+    assertEq(this.values.j, "J");
+    assertEq(this.values.l, "L");
+    assertEq(this.values.m, "M");
+    assertEq(this.values.n, "N");
+    assertEq(this.values.o, "O");
+    assertEq(this.values.q, "Q");
+    assertEq(this.values.s, "S");
+    assertEq(this.values.t, "T");
+    assertEq(this.values.u, "U");
+    assertEq(this.values.v, "V");
+    assertEq(this.values.x, "X");
+    assertEq(this.values.z, "Z");
+    assertEq(this.values.length, 2);
+  }
+}
+
+new C2();
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/order.js
@@ -0,0 +1,745 @@
+var BUGNUMBER = 1204028;
+var summary = "Destructuring should evaluate lhs reference before rhs";
+
+print(BUGNUMBER + ": " + summary);
+
+let storage = {
+  clear() {
+    this.values = {};
+  }
+};
+storage.clear();
+let obj = new Proxy(storage, {
+  set(that, name, value) {
+    log("lhs set " + name);
+    storage.values[name] = value;
+  }
+});
+
+let logs = [];
+function log(x) {
+  logs.push(x);
+}
+
+function clear() {
+  logs = [];
+  storage.clear();
+}
+
+let unwrapMap = new Map();
+function unwrap(maybeWrapped) {
+  if (unwrapMap.has(maybeWrapped))
+    return unwrapMap.get(maybeWrapped);
+  return maybeWrapped;
+}
+function ToString(name) {
+  if (name == Symbol.iterator)
+    return "@@iterator";
+  return String(name);
+}
+function logger(obj, prefix=[]) {
+  let wrapped = new Proxy(obj, {
+    get(that, name) {
+      if (name == "return") {
+        // FIXME: Bug 1147371.
+        // We ignore IteratorClose for now.
+        return obj[name];
+      }
+
+      let names = prefix.concat(ToString(name));
+      log("rhs get " + names.join("::"));
+      let v = obj[name];
+      if (typeof v === "object" || typeof v === "function")
+        return logger(v, names);
+      return v;
+    },
+    apply(that, thisArg, args) {
+      let names = prefix.slice();
+      log("rhs call " + names.join("::"));
+      let v = obj.apply(unwrap(thisArg), args);
+      if (typeof v === "object" || typeof v === "function") {
+        names[names.length - 1] += "()";
+        return logger(v, names);
+      }
+      return v;
+    }
+  });
+  unwrapMap.set(wrapped, obj);
+  return wrapped;
+}
+
+// Array.
+
+clear();
+[
+  ( log("lhs before obj a"), obj ).a
+] = logger(["A"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a, "A");
+
+clear();
+[
+  ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ]
+] = logger(["A"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a, "A");
+
+// Array rest.
+
+clear();
+[
+  ...( log("lhs before obj a"), obj ).a
+] = logger(["A", "B", "C"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a.join(","), "A,B,C");
+
+clear();
+[
+  ...( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ]
+] = logger(["A", "B", "C"]);;
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a.join(","), "A,B,C");
+
+// Array combined.
+
+clear();
+[
+  ( log("lhs before obj a"), obj ).a,
+  ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ],
+  ...( log("lhs before obj c"), obj )[ (log("lhs before name c"), "c") ]
+] = logger(["A", "B", "C"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "lhs set a",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "lhs set b",
+
+           "lhs before obj c",
+           "lhs before name c",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "lhs set c",
+         ].join(","));
+assertEq(storage.values.a, "A");
+assertEq(storage.values.b, "B");
+assertEq(storage.values.c.join(","), "C");
+
+// Object.
+
+clear();
+({
+  a: ( log("lhs before obj a"), obj ).a
+} = logger({a: "A"}));
+assertEq(logs.join(","),
+         [
+           "lhs before obj a",
+           "rhs get a",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a, "A");
+
+clear();
+({
+  a: ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ]
+} = logger({a: "A"}));
+assertEq(logs.join(","),
+         [
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get a",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a, "A");
+
+// Object combined.
+
+clear();
+({
+  a: ( log("lhs before obj a"), obj ).a,
+  b: ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ]
+} = logger({a: "A", b: "B"}));
+assertEq(logs.join(","),
+         [
+           "lhs before obj a",
+           "rhs get a",
+           "lhs set a",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get b",
+           "lhs set b",
+         ].join(","));
+assertEq(storage.values.a, "A");
+assertEq(storage.values.b, "B");
+
+// == Nested ==
+
+// Array -> Array
+
+clear();
+[
+  [
+    ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ],
+    ...( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ]
+  ]
+] = logger([["A", "B"]]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next()::value::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+           "lhs set a",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "lhs set b",
+         ].join(","));
+assertEq(storage.values.a, "A");
+assertEq(storage.values.b.length, 1);
+assertEq(storage.values.b[0], "B");
+
+// Array rest -> Array
+
+clear();
+[
+  ...[
+    ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ],
+    ...( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ]
+  ]
+] = logger(["A", "B"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "lhs set a",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "lhs set b",
+         ].join(","));
+assertEq(storage.values.a, "A");
+assertEq(storage.values.b.join(","), "B");
+
+// Array -> Object
+clear();
+[
+  {
+    a: ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ]
+  }
+] = logger([{a: "A"}]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get @@iterator()::next()::value::a",
+           "lhs set a",
+         ].join(","));
+assertEq(storage.values.a, "A");
+
+// Array rest -> Object
+clear();
+[
+  ...{
+    0: ( log("lhs before obj 0"), obj )[ (log("lhs before name 0"), "0") ],
+    1: ( log("lhs before obj 1"), obj )[ (log("lhs before name 1"), "1") ],
+    length: ( log("lhs before obj length"), obj )[ (log("lhs before name length"), "length") ],
+  }
+] = logger(["A", "B"]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+
+           "lhs before obj 0",
+           "lhs before name 0",
+           "lhs set 0",
+
+           "lhs before obj 1",
+           "lhs before name 1",
+           "lhs set 1",
+
+           "lhs before obj length",
+           "lhs before name length",
+           "lhs set length",
+         ].join(","));
+assertEq(storage.values["0"], "A");
+assertEq(storage.values["1"], "B");
+assertEq(storage.values.length, 2);
+
+// Object -> Array
+clear();
+({
+  a: [
+    ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ]
+  ]
+} = logger({a: ["B"]}));
+assertEq(logs.join(","),
+         [
+           "rhs get a",
+           "rhs get a::@@iterator",
+           "rhs call a::@@iterator",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get a::@@iterator()::next",
+           "rhs call a::@@iterator()::next",
+           "rhs get a::@@iterator()::next()::done",
+           "rhs get a::@@iterator()::next()::value",
+           "lhs set b",
+         ].join(","));
+assertEq(storage.values.b, "B");
+
+// Object -> Object
+clear();
+({
+  a: {
+    b: ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ]
+  }
+} = logger({a: {b: "B"}}));
+assertEq(logs.join(","),
+         [
+           "rhs get a",
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get a::b",
+           "lhs set b",
+         ].join(","));
+assertEq(storage.values.b, "B");
+
+// All combined
+
+clear();
+[
+  ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ],
+  [
+    ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ],
+    {
+      c: ( log("lhs before obj c"), obj )[ (log("lhs before name c"), "c") ],
+      d: {
+        e: ( log("lhs before obj e"), obj )[ (log("lhs before name e"), "e") ],
+        f: [
+          ( log("lhs before obj g"), obj )[ (log("lhs before name g"), "g") ]
+        ]
+      }
+    }
+  ],
+  {
+    h: ( log("lhs before obj h"), obj )[ (log("lhs before name h"), "h") ],
+    i: [
+      ( log("lhs before obj j"), obj )[ (log("lhs before name j"), "j") ],
+      {
+        k: [
+          ( log("lhs before obj l"), obj )[ (log("lhs before name l"), "l") ]
+        ]
+      }
+    ]
+  },
+  ...[
+    ( log("lhs before obj m"), obj )[ (log("lhs before name m"), "m") ],
+    [
+      ( log("lhs before obj n"), obj )[ (log("lhs before name n"), "n") ],
+      {
+        o: ( log("lhs before obj o"), obj )[ (log("lhs before name o"), "o") ],
+        p: {
+          q: ( log("lhs before obj q"), obj )[ (log("lhs before name q"), "q") ],
+          r: [
+            ( log("lhs before obj s"), obj )[ (log("lhs before name s"), "s") ]
+          ]
+        }
+      }
+    ],
+    ...{
+      0: ( log("lhs before obj t"), obj )[ (log("lhs before name t"), "t") ],
+      1: [
+        ( log("lhs before obj u"), obj )[ (log("lhs before name u"), "u") ],
+        {
+          v: ( log("lhs before obj v"), obj )[ (log("lhs before name v"), "v") ],
+          w: {
+            x: ( log("lhs before obj x"), obj )[ (log("lhs before name x"), "x") ],
+            y: [
+              ( log("lhs before obj z"), obj )[ (log("lhs before name z"), "z") ]
+            ]
+          }
+        }
+      ],
+      length: ( log("lhs before obj length"), obj )[ (log("lhs before name length"), "length") ],
+    }
+  ]
+] = logger(["A",
+            ["B", {c: "C", d: {e: "E", f: ["G"]}}],
+            {h: "H", i: ["J", {k: ["L"]}]},
+            "M",
+            ["N", {o: "O", p: {q: "Q", r: ["S"]}}],
+            "T", ["U", {v: "V", w: {x: "X", y: ["Z"]}}]]);
+assertEq(logs.join(","),
+         [
+           "rhs get @@iterator",
+           "rhs call @@iterator",
+
+           "lhs before obj a",
+           "lhs before name a",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "lhs set a",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next()::value::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator",
+
+           "lhs before obj b",
+           "lhs before name b",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+           "lhs set b",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+           "lhs before obj c",
+           "lhs before name c",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::c",
+           "lhs set c",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d",
+
+           "lhs before obj e",
+           "lhs before name e",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::e",
+           "lhs set e",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator",
+
+           "lhs before obj g",
+           "lhs before name g",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::value",
+           "lhs set g",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+
+           "lhs before obj h",
+           "lhs before name h",
+           "rhs get @@iterator()::next()::value::h",
+           "lhs set h",
+
+           "rhs get @@iterator()::next()::value::i",
+           "rhs get @@iterator()::next()::value::i::@@iterator",
+           "rhs call @@iterator()::next()::value::i::@@iterator",
+
+           "lhs before obj j",
+           "lhs before name j",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::i::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value",
+           "lhs set j",
+
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::i::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value",
+
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator",
+           "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator",
+
+           "lhs before obj l",
+           "lhs before name l",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::value",
+           "lhs set l",
+
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value",
+           "rhs get @@iterator()::next",
+           "rhs call @@iterator()::next",
+           "rhs get @@iterator()::next()::done",
+
+           "lhs before obj m",
+           "lhs before name m",
+           "lhs set m",
+
+           "rhs get @@iterator()::next()::value::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator",
+
+           "lhs before obj n",
+           "lhs before name n",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+           "lhs set n",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+           "lhs before obj o",
+           "lhs before name o",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::o",
+           "lhs set o",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p",
+
+           "lhs before obj q",
+           "lhs before name q",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::q",
+           "lhs set q",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator",
+
+           "lhs before obj s",
+           "lhs before name s",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::value",
+           "lhs set s",
+
+           "lhs before obj t",
+           "lhs before name t",
+           "lhs set t",
+
+           "rhs get @@iterator()::next()::value::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator",
+
+           "lhs before obj u",
+           "lhs before name u",
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+           "lhs set u",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value",
+
+           "lhs before obj v",
+           "lhs before name v",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::v",
+           "lhs set v",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w",
+
+           "lhs before obj x",
+           "lhs before name x",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::x",
+           "lhs set x",
+
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator",
+
+           "lhs before obj z",
+           "lhs before name z",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next",
+           "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::done",
+           "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::value",
+           "lhs set z",
+
+           "lhs before obj length",
+           "lhs before name length",
+           "lhs set length",
+         ].join(","));
+assertEq(storage.values.a, "A");
+assertEq(storage.values.b, "B");
+assertEq(storage.values.c, "C");
+assertEq(storage.values.e, "E");
+assertEq(storage.values.g, "G");
+assertEq(storage.values.h, "H");
+assertEq(storage.values.j, "J");
+assertEq(storage.values.l, "L");
+assertEq(storage.values.m, "M");
+assertEq(storage.values.n, "N");
+assertEq(storage.values.o, "O");
+assertEq(storage.values.q, "Q");
+assertEq(storage.values.s, "S");
+assertEq(storage.values.t, "T");
+assertEq(storage.values.u, "U");
+assertEq(storage.values.v, "V");
+assertEq(storage.values.x, "X");
+assertEq(storage.values.z, "Z");
+assertEq(storage.values.length, 2);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1863,17 +1863,16 @@ CASE(EnableInterruptsPseudoOpcode)
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED183)
 CASE(JSOP_UNUSED187)
 CASE(JSOP_UNUSED192)
 CASE(JSOP_UNUSED209)
 CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED211)
 CASE(JSOP_UNUSED219)
 CASE(JSOP_UNUSED220)
 CASE(JSOP_UNUSED221)
@@ -2196,16 +2195,26 @@ CASE(JSOP_PICK)
     unsigned i = GET_UINT8(REGS.pc);
     MOZ_ASSERT(REGS.stackDepth() >= i + 1);
     Value lval = REGS.sp[-int(i + 1)];
     memmove(REGS.sp - (i + 1), REGS.sp - i, sizeof(Value) * i);
     REGS.sp[-1] = lval;
 }
 END_CASE(JSOP_PICK)
 
+CASE(JSOP_UNPICK)
+{
+    int i = GET_UINT8(REGS.pc);
+    MOZ_ASSERT(REGS.stackDepth() >= unsigned(i) + 1);
+    Value lval = REGS.sp[-1];
+    memmove(REGS.sp - i, REGS.sp - (i + 1), sizeof(Value) * i);
+    REGS.sp[-(i + 1)] = lval;
+}
+END_CASE(JSOP_UNPICK)
+
 CASE(JSOP_BINDGNAME)
 CASE(JSOP_BINDNAME)
 {
     JSOp op = JSOp(*REGS.pc);
     ReservedRooted<JSObject*> envChain(&rootObject0);
     if (op == JSOP_BINDNAME || script->hasNonSyntacticScope())
         envChain.set(REGS.fp()->environmentChain());
     else
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1875,18 +1875,24 @@ 1234567890123456789012345678901234567890
      * name of 'fun' to 'name' with prefix if any, and pushes 'fun' back onto
      * the stack.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint8_t prefixKind
      *   Stack: fun, name => fun
      */ \
     macro(JSOP_SETFUNNAME,    182,"setfunname", NULL,     2,  2,  1,  JOF_UINT8) \
-    macro(JSOP_UNUSED183,     183,"unused183",  NULL,     1,  0,  0,  JOF_BYTE) \
-    \
+    /*
+     * Moves the top of the stack value under the nth element of the stack.
+     *   Category: Operators
+     *   Type: Stack Operations
+     *   Operands: uint8_t n
+     *   Stack: v[n], v[n-1], ..., v[1], v[0] => v[0], v[n], v[n-1], ..., v[1]
+     */ \
+    macro(JSOP_UNPICK,        183,"unpick",     NULL,     2,  0,  0,  JOF_UINT8) \
     /*
      * Pops the top of stack value, pushes property of it onto the stack.
      *
      * Like JSOP_GETPROP but for call context.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
      *   Stack: obj => obj[name]