From 3ee73ca14cec9ac99ebee938d76650ad11aa98da Mon Sep 17 00:00:00 2001
From: janekptacijarabaci <janekptacijarabaci@seznam.cz>
Date: Tue, 20 Mar 2018 10:27:23 +0100
Subject: Bug 1322314 - Disallow emitting ParseNode twice

Issue #73
[Depends on] Bug 1147371: Implement IteratorClose
---
 js/src/frontend/BytecodeEmitter.cpp | 29 ++++-------------------------
 js/src/frontend/BytecodeEmitter.h   |  4 +---
 js/src/frontend/ParseNode.h         |  4 +---
 3 files changed, 6 insertions(+), 31 deletions(-)

(limited to 'js/src/frontend')

diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index acf734794..ee26d0c43 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6975,14 +6975,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
     RootedFunction fun(cx, funbox->function());
     RootedAtom name(cx, fun->explicitName());
     MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
-    MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto);
 
     /*
      * Set the |wasEmitted| flag in the funbox once the function has been
      * emitted. Function definitions that need hoisting to the top of the
      * function will be seen by emitFunction in two places.
      */
-    if (funbox->wasEmitted && pn->functionIsHoisted()) {
+    if (funbox->wasEmitted) {
         // Annex B block-scoped functions are hoisted like any other
         // block-scoped function to the top of their scope. When their
         // definitions are seen for the second time, we need to emit the
@@ -7111,7 +7110,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
         }
 
         if (needsProto) {
-            MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
+            MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
             pn->setOp(JSOP_FUNWITHPROTO);
         }
 
@@ -10047,15 +10046,6 @@ CGConstList::finish(ConstArray* array)
         array->vector[i] = list[i];
 }
 
-bool
-CGObjectList::isAdded(ObjectBox* objbox)
-{
-    // An objbox added to CGObjectList as non-first element has non-null
-    // emitLink member.  The first element has null emitLink.
-    // Check for firstbox to cover the first element.
-    return objbox->emitLink || objbox == firstbox;
-}
-
 /*
  * Find the index of the given object for code generator.
  *
@@ -10067,15 +10057,9 @@ CGObjectList::isAdded(ObjectBox* objbox)
 unsigned
 CGObjectList::add(ObjectBox* objbox)
 {
-    if (isAdded(objbox))
-        return indexOf(objbox->object);
-
+    MOZ_ASSERT(!objbox->emitLink);
     objbox->emitLink = lastbox;
     lastbox = objbox;
-
-    // See the comment in CGObjectList::isAdded.
-    if (!firstbox)
-        firstbox = objbox;
     return length++;
 }
 
@@ -10102,12 +10086,7 @@ CGObjectList::finish(ObjectArray* array)
         MOZ_ASSERT(!*cursor);
         MOZ_ASSERT(objbox->object->isTenured());
         *cursor = objbox->object;
-
-        ObjectBox* tmp = objbox->emitLink;
-        // Clear emitLink for CGObjectList::isAdded.
-        objbox->emitLink = nullptr;
-        objbox = tmp;
-    } while (objbox != nullptr);
+    } while ((objbox = objbox->emitLink) != nullptr);
     MOZ_ASSERT(cursor == array->vector);
 }
 
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 9a2ddb568..4b3750dd5 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -43,12 +43,10 @@ class CGConstList {
 
 struct CGObjectList {
     uint32_t            length;     /* number of emitted so far objects */
-    ObjectBox*          firstbox;  /* first emitted object */
     ObjectBox*          lastbox;   /* last emitted object */
 
-    CGObjectList() : length(0), firstbox(nullptr), lastbox(nullptr) {}
+    CGObjectList() : length(0), lastbox(nullptr) {}
 
-    bool isAdded(ObjectBox* objbox);
     unsigned add(ObjectBox* objbox);
     unsigned indexOf(JSObject* obj);
     void finish(ObjectArray* array);
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index ff26279af..c58dab431 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -649,14 +649,12 @@ class ParseNode
         MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
         MOZ_ASSERT(isOp(JSOP_LAMBDA) ||        // lambda, genexpr
                    isOp(JSOP_LAMBDA_ARROW) ||  // arrow function
-                   isOp(JSOP_FUNWITHPROTO) ||  // already emitted lambda with needsProto
                    isOp(JSOP_DEFFUN) ||        // non-body-level function statement
                    isOp(JSOP_NOP) ||           // body-level function stmt in global code
                    isOp(JSOP_GETLOCAL) ||      // body-level function stmt in function code
                    isOp(JSOP_GETARG) ||        // body-level function redeclaring formal
                    isOp(JSOP_INITLEXICAL));    // block-level function stmt
-        return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) &&
-               !isOp(JSOP_FUNWITHPROTO) && !isOp(JSOP_DEFFUN);
+        return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
     }
 
     /*
-- 
cgit v1.2.3


From caa2a53c402c7b509e9939e9aefe595dc0dbe516 Mon Sep 17 00:00:00 2001
From: janekptacijarabaci <janekptacijarabaci@seznam.cz>
Date: Tue, 20 Mar 2018 10:46:22 +0100
Subject: Bug 1322314 - Do not emit ParseNode twice in
 BytecodeEmitter::emitDestructuringOpsArray

Issue #73
[Depends on] Bug 1147371: Implement IteratorClose
---
 js/src/frontend/BytecodeEmitter.cpp | 257 +++++++++++++++++-------------------
 js/src/frontend/BytecodeEmitter.h   |   1 -
 2 files changed, 119 insertions(+), 139 deletions(-)

(limited to 'js/src/frontend')

diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index ee26d0c43..a68fe8538 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4462,13 +4462,6 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
     return true;
 }
 
-bool
-BytecodeEmitter::emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav)
-{
-    TDZCheckCache tdzCache(this);
-    return emitDestructuringLHS(target, flav);
-}
-
 bool
 BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
 {
@@ -4739,7 +4732,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //
     //   let x, y;
     //   let a, b, c, d;
-    //   let tmp, done, iter, result; // stack values
+    //   let iter, result, done, value; // stack values
     //
     //   iter = x[Symbol.iterator]();
     //
@@ -4747,115 +4740,113 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //   result = iter.next();
     //   done = result.done;
     //
-    //   if (done) {
-    //     a = undefined;
-    //
-    //     result = undefined;
-    //     done = true;
-    //   } else {
-    //     a = result.value;
+    //   if (done)
+    //     value = undefined;
+    //   else
+    //     value = result.value;
     //
-    //     // Do next element's .next() and .done access here
-    //     result = iter.next();
-    //     done = result.done;
-    //   }
+    //   a = value;
     //
     //   // ==== emitted by loop for b ====
     //   if (done) {
-    //     b = undefined;
-    //
-    //     result = undefined;
-    //     done = true;
+    //     value = undefined;
     //   } else {
-    //     b = result.value;
-    //
     //     result = iter.next();
     //     done = result.done;
+    //     if (done)
+    //       value = undefined;
+    //     else
+    //       value = result.value;
     //   }
     //
+    //   b = value;
+    //
     //   // ==== emitted by loop for elision ====
     //   if (done) {
-    //     result = undefined
-    //     done = true
+    //     value = undefined;
     //   } else {
-    //     result.value;
-    //
     //     result = iter.next();
     //     done = result.done;
+    //     if (done)
+    //       value = undefined;
+    //     else
+    //       value = result.value;
     //   }
     //
     //   // ==== emitted by loop for c ====
     //   if (done) {
-    //     c = y;
+    //     value = undefined;
     //   } else {
-    //     tmp = result.value;
-    //     if (tmp === undefined)
-    //       tmp = y;
-    //     c = tmp;
-    //
-    //     // Don't do next element's .next() and .done access if
-    //     // this is the last non-spread element.
+    //     result = iter.next();
+    //     done = result.done;
+    //     if (done)
+    //       value = undefined;
+    //     else
+    //       value = result.value;
     //   }
     //
+    //   if (value === undefined)
+    //     value = y;
+    //
+    //   c = value;
+    //
     //   // ==== emitted by loop for d ====
-    //   if (done) {
-    //     // Assing empty array when completed
-    //     d = [];
-    //   } else {
-    //     d = [...iter];
-    //   }
+    //   if (done)
+    //     value = [];
+    //   else
+    //     value = [...iter];
+    //
+    //   d = value;
 
-    /*
-     * Use an iterator to destructure the RHS, instead of index lookup. We
-     * must leave the *original* value on the stack.
-     */
+    // 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
+    if (!emitIterator())                                          // ... OBJ ITER
         return false;
-    bool needToPopIterator = true;
 
     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)) {
             IfThenElseEmitter ifThenElse(this);
             if (!isHead) {
                 // If spread is not the first element of the pattern,
                 // iterator can already be completed.
-                if (!ifThenElse.emitIfElse())                     // ... OBJ? ITER
+                //                                                   ... OBJ ITER DONE
+                if (!ifThenElse.emitIfElse())                     // ... OBJ ITER
                     return false;
 
-                if (!emit1(JSOP_POP))                             // ... OBJ?
-                    return false;
-                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ? ARRAY
+                if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ ITER ARRAY
                     return false;
-                if (!emitDestructuringLHSInBranch(member, flav))  // ... OBJ?
-                    return false;
-
-                if (!ifThenElse.emitElse())                       // ... OBJ? ITER
+                if (!ifThenElse.emitElse())                       // ... OBJ ITER
                     return false;
             }
 
             // If iterator is not completed, create a new array with the rest
             // of the iterator.
-            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ? ITER ARRAY
+            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER
                 return false;
-            if (!emitNumberOp(0))                                 // ... OBJ? ITER ARRAY INDEX
+            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER ITER ARRAY
                 return false;
-            if (!emitSpread())                                    // ... OBJ? ARRAY INDEX
+            if (!emitNumberOp(0))                                 // ... OBJ ITER ITER ARRAY INDEX
                 return false;
-            if (!emit1(JSOP_POP))                                 // ... OBJ? ARRAY
+            if (!emitSpread())                                    // ... OBJ ITER ARRAY INDEX
                 return false;
-            if (!emitDestructuringLHSInBranch(member, flav))      // ... OBJ?
+            if (!emit1(JSOP_POP))                                 // ... OBJ ITER ARRAY
                 return false;
 
             if (!isHead) {
                 if (!ifThenElse.emitEnd())
                     return false;
-                MOZ_ASSERT(ifThenElse.popped() == 1);
+                MOZ_ASSERT(ifThenElse.pushed() == 1);
             }
-            needToPopIterator = false;
-            MOZ_ASSERT(!member->pn_next);
+
+            if (!emitDestructuringLHS(member, flav))              // ... OBJ ITER
+                return false;
+
+            MOZ_ASSERT(!hasNext);
             break;
         }
 
@@ -4867,110 +4858,100 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
         }
 
         bool isElision = subpattern->isKind(PNK_ELISION);
-        bool hasNextNonSpread = member->pn_next && !member->pn_next->isKind(PNK_SPREAD);
-        bool hasNextSpread = member->pn_next && member->pn_next->isKind(PNK_SPREAD);
 
         MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD));
 
-        auto emitNext = [pattern](ExclusiveContext* cx, BytecodeEmitter* bce) {
-            if (!bce->emit1(JSOP_DUP))                            // ... OBJ? ITER ITER
-                return false;
-            if (!bce->emitIteratorNext(pattern))                  // ... OBJ? ITER RESULT
+        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;
+            }
+            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER ?DONE
                 return false;
-            if (!bce->emit1(JSOP_DUP))                            // ... OBJ? ITER RESULT RESULT
+
+            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER ?DONE UNDEF
                 return false;
-            if (!bce->emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ? ITER RESULT DONE?
+            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER ?DONE UNDEF
                 return false;
-            return true;
-        };
 
-        if (isHead) {
-            if (!emitNext(cx, this))                              // ... OBJ? ITER RESULT DONE?
+            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER ?DONE
                 return false;
+
+            if (hasNext) {
+                if (!emit1(JSOP_POP))                             // ... OBJ ITER
+                    return false;
+            }
         }
 
-        IfThenElseEmitter ifThenElse(this);
-        if (!ifThenElse.emitIfElse())                             // ... OBJ? ITER RESULT
+        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER ITER
             return false;
-
-        if (!emit1(JSOP_POP))                                     // ... OBJ? ITER
+        if (!emitIteratorNext(pattern))                           // ... OBJ ITER RESULT
             return false;
-        if (pndefault) {
-            // Emit only pndefault tree here, as undefined check in emitDefault
-            // should always be true.
-            if (!emitInitializerInBranch(pndefault, subpattern))  // ... OBJ? ITER VALUE
+        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER RESULT DONE
+            return false;
+
+        if (hasNext) {
+            if (!emit1(JSOP_DUP))                                 // ... OBJ ITER RESULT DONE DONE
                 return false;
-        } else {
-            if (!isElision) {
-                if (!emit1(JSOP_UNDEFINED))                       // ... OBJ? ITER UNDEFINED
-                    return false;
-                if (!emit1(JSOP_NOP_DESTRUCTURING))
-                    return false;
-            }
         }
-        if (!isElision) {
-            if (!emitDestructuringLHSInBranch(subpattern, flav))  // ... OBJ? ITER
-                return false;
-        } else if (pndefault) {
-            if (!emit1(JSOP_POP))                                 // ... OBJ? ITER
+
+        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;
 
-        // Setup next element's result when the iterator is done.
-        if (hasNextNonSpread) {
-            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ? ITER RESULT
-                return false;
-            if (!emit1(JSOP_NOP_DESTRUCTURING))
-                return false;
-            if (!emit1(JSOP_TRUE))                                // ... OBJ? ITER RESULT DONE?
-                return false;
-        } else if (hasNextSpread) {
-            if (!emit1(JSOP_TRUE))                                // ... OBJ? ITER DONE?
+        if (!ifDone.emitElse())                                   // ... OBJ ITER RESULT ?DONE
+            return false;
+
+        if (hasNext) {
+            if (!emit1(JSOP_SWAP))                                // ... OBJ ITER ?DONE RESULT
                 return false;
         }
-
-        if (!ifThenElse.emitElse())                               // ... OBJ? ITER RESULT
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ ITER ?DONE VALUE
             return false;
 
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ? ITER VALUE
+        if (!ifDone.emitEnd())
             return false;
+        MOZ_ASSERT(ifDone.pushed() == 0);
 
-        if (pndefault) {
-            if (!emitDefault(pndefault, subpattern))              // ... OBJ? ITER VALUE
+        if (!isHead) {
+            if (!ifAlreadyDone.emitEnd())
                 return false;
+            MOZ_ASSERT(ifAlreadyDone.pushed() == 1);
         }
 
-        if (!isElision) {
-            if (!emitDestructuringLHSInBranch(subpattern, flav))  // ... OBJ? ITER
-                return false;
-        } else {
-            if (!emit1(JSOP_POP))                                 // ... OBJ? ITER
+        if (pndefault) {
+            if (!emitDefault(pndefault, subpattern))              // ... OBJ ITER ?DONE VALUE
                 return false;
         }
 
-        // Setup next element's result when the iterator is not done.
-        if (hasNextNonSpread) {
-            if (!emitNext(cx, this))                              // ... OBJ? ITER RESULT DONE?
+        if (!isElision) {
+            if (!emitDestructuringLHS(subpattern, flav))          // ... OBJ ITER ?DONE
                 return false;
-        } else if (hasNextSpread) {
-            if (!emit1(JSOP_FALSE))                               // ... OBJ? ITER DONE?
+        } else {
+            if (!emit1(JSOP_POP))                                 // ... OBJ ITER ?DONE
                 return false;
         }
-
-        if (!ifThenElse.emitEnd())
-            return false;
-        if (hasNextNonSpread)
-            MOZ_ASSERT(ifThenElse.pushed() == 1);
-        else if (hasNextSpread)
-            MOZ_ASSERT(ifThenElse.pushed() == 0);
-        else
-            MOZ_ASSERT(ifThenElse.popped() == 1);
     }
 
-    if (needToPopIterator) {
-        if (!emit1(JSOP_POP))                                     // ... OBJ?
-            return false;
-    }
+    if (!emit1(JSOP_POP))                                         // ... OBJ
+        return false;
 
     return true;
 }
@@ -6118,7 +6099,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted)
             return false;
         if (!emit1(JSOP_DUP))                             // ITER ARR I RESULT RESULT
             return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER ARR I RESULT DONE?
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER ARR I RESULT DONE
             return false;
 
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT
@@ -6307,7 +6288,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
             return false;
         if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
             return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT DONE?
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT DONE
             return false;
 
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
@@ -6803,7 +6784,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
         return false;
     if (!emit1(JSOP_DUP))                                 // ITER RESULT RESULT
         return false;
-    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT DONE?
+    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT DONE
         return false;
 
     JumpList beq;
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 4b3750dd5..f09b529ed 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -646,7 +646,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     // 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);
-    MOZ_MUST_USE bool emitDestructuringLHSInBranch(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
-- 
cgit v1.2.3


From 2a57d73c3b5304be3f9be51018a1bbee79f007e2 Mon Sep 17 00:00:00 2001
From: janekptacijarabaci <janekptacijarabaci@seznam.cz>
Date: Tue, 20 Mar 2018 12:40:00 +0100
Subject: Bug 1204028: Evaluate LHS reference before RHS in destructuring

Issue #73
[Depends on] Bug 1147371: Implement IteratorClose
---
 js/src/frontend/BytecodeEmitter.cpp | 277 ++++++++++++++++++++++++------------
 js/src/frontend/BytecodeEmitter.h   |  18 ++-
 2 files changed, 202 insertions(+), 93 deletions(-)

(limited to 'js/src/frontend')

diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index a68fe8538..a4cfeb753 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4325,7 +4325,69 @@ BytecodeEmitter::emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitt
 }
 
 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
@@ -4401,44 +4463,28 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
           }
 
           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;
@@ -4451,7 +4497,7 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
             break;
 
           default:
-            MOZ_CRASH("emitDestructuringLHS: bad lhs kind");
+            MOZ_CRASH("emitSetOrInitializeDestructuring: bad lhs kind");
         }
 
         // Pop the assigned value.
@@ -4732,11 +4778,13 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //
     //   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;
     //
@@ -4745,9 +4793,11 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //   else
     //     value = result.value;
     //
-    //   a = value;
+    //   SetOrInitialize(lref, value);
     //
     //   // ==== emitted by loop for b ====
+    //   lref = GetReference(b);
+    //
     //   if (done) {
     //     value = undefined;
     //   } else {
@@ -4759,7 +4809,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //       value = result.value;
     //   }
     //
-    //   b = value;
+    //   SetOrInitialize(lref, value);
     //
     //   // ==== emitted by loop for elision ====
     //   if (done) {
@@ -4774,6 +4824,8 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //   }
     //
     //   // ==== emitted by loop for c ====
+    //   lref = GetReference(c);
+    //
     //   if (done) {
     //     value = undefined;
     //   } else {
@@ -4788,15 +4840,17 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
     //   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.
@@ -4810,31 +4864,40 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
         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
+            if (!emitDupAt(emitted))                              // ... OBJ ITER *LREF ITER
                 return false;
-            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER ITER ARRAY
+            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ ITER *LREF ITER ARRAY
                 return false;
-            if (!emitNumberOp(0))                                 // ... OBJ ITER ITER ARRAY INDEX
+            if (!emitNumberOp(0))                                 // ... OBJ ITER *LREF ITER ARRAY INDEX
                 return false;
-            if (!emitSpread())                                    // ... OBJ ITER ARRAY INDEX
+            if (!emitSpread())                                    // ... OBJ ITER *LREF ARRAY INDEX
                 return false;
-            if (!emit1(JSOP_POP))                                 // ... OBJ ITER ARRAY
+            if (!emit1(JSOP_POP))                                 // ... OBJ ITER *LREF ARRAY
                 return false;
 
             if (!isHead) {
@@ -4843,7 +4906,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
                 MOZ_ASSERT(ifThenElse.pushed() == 1);
             }
 
-            if (!emitDestructuringLHS(member, flav))              // ... OBJ ITER
+            if (!emitSetOrInitializeDestructuring(member, flav))  // ... OBJ ITER
                 return false;
 
             MOZ_ASSERT(!hasNext);
@@ -4861,69 +4924,91 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
 
         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
+            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ ITER ?DONE *LREF
                 return false;
 
-            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER ?DONE UNDEF
+            if (!emit1(JSOP_UNDEFINED))                           // ... OBJ ITER ?DONE *LREF UNDEF
                 return false;
-            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER ?DONE UNDEF
+            if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ ITER ?DONE *LREF UNDEF
                 return false;
 
-            if (!ifAlreadyDone.emitElse())                        // ... OBJ ITER ?DONE
+            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
+        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 RESULT RESULT
+        if (!emit1(JSOP_DUP))                                     // ... OBJ ITER *LREF RESULT RESULT
             return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))          // ... OBJ ITER RESULT DONE
+        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
+        if (!ifDone.emitIfElse())                                 // ... OBJ ITER ?DONE *LREF RESULT
             return false;
 
-        if (hasNext) {
-            if (!emit1(JSOP_SWAP))                                // ... OBJ ITER ?DONE RESULT
-                return false;
-        }
-        if (!emit1(JSOP_POP))                                     // ... OBJ ITER ?DONE
+        if (!emit1(JSOP_POP))                                     // ... OBJ ITER ?DONE *LREF
             return false;
-        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER ?DONE UNDEF
+        if (!emit1(JSOP_UNDEFINED))                               // ... OBJ ITER ?DONE *LREF UNDEF
             return false;
-        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER ?DONE UNDEF
+        if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ ITER ?DONE *LREF UNDEF
             return false;
 
-        if (!ifDone.emitElse())                                   // ... OBJ ITER RESULT ?DONE
+        if (!ifDone.emitElse())                                   // ... OBJ ITER ?DONE *LREF RESULT
             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 (!emitAtomOp(cx->names().value, JSOP_GETPROP))         // ... OBJ ITER ?DONE *LREF VALUE
             return false;
 
         if (!ifDone.emitEnd())
@@ -4937,13 +5022,16 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
         }
 
         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
+            if (!emitSetOrInitializeDestructuring(subpattern,
+                                                  flav))          // ... OBJ ITER ?DONE
+            {
                 return false;
+            }
         } else {
             if (!emit1(JSOP_POP))                                 // ... OBJ ITER ?DONE
                 return false;
@@ -4975,27 +5063,43 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
         return false;
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
-        // Duplicate the value being destructured to use as a reference base.
-        if (!emit1(JSOP_DUP))                                     // ... RHS RHS
+        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 (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();
@@ -5005,33 +5109,30 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
                 // 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))
+            if (!emitDefault(subpattern->pn_right, lhs))          // ... RHS *LREF VALUE
                 return false;
-            subpattern = subpattern->pn_left;
         }
 
-        // Destructure PROP per this member's subpattern.
-        if (!emitDestructuringLHS(subpattern, flav))
+        // Destructure PROP per this member's lhs.
+        if (!emitSetOrInitializeDestructuring(subpattern, flav))  // ... RHS
             return false;
     }
 
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index f09b529ed..066c06672 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -609,7 +609,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     // 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);
@@ -642,10 +642,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
         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
-- 
cgit v1.2.3