Backed out changeset b28ef89ebda2 (bug 1147371) since it seems this made browser_console_addonsdk_loader_exception.js | Test timed out - more worse
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 13 Jan 2017 11:13:17 +0100
changeset 374318 feb27da5b04c17a194400bd22ee94b7398a13fe3
parent 374317 831c8cfd251217635660ab513b2409e0987e936b
child 374319 f248d089469d2d2a1edf8cc4d05a7203254a4698
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1147371
milestone53.0a1
backs outb28ef89ebda20992f764456aaf6eebd5421eb56f
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
Backed out changeset b28ef89ebda2 (bug 1147371) since it seems this made browser_console_addonsdk_loader_exception.js | Test timed out - more worse
js/src/builtin/TypedArray.js
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit/JitFrames.cpp
js/src/js.msg
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsscript.h
js/src/shell/js.cpp
js/src/tests/ecma_6/Statements/for-of-iterator-close.js
js/src/tests/ecma_6/shell.js
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1432,17 +1432,17 @@ function TypedArrayStaticFrom(source, ma
         var values = new List();
 
         // 22.2.2.1.1 IterableToList, steps 3-4.
         var i = 0;
         while (true) {
             // 22.2.2.1.1 IterableToList, step 4.a.
             var next = callContentFunction(iterator.next, iterator);
             if (!IsObject(next))
-                ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next");
+                ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
 
             // 22.2.2.1.1 IterableToList, step 4.b.
             if (next.done)
                 break;
             values[i++] = next.value;
         }
 
         // Step 7.b.
@@ -1559,17 +1559,17 @@ function IterableToList(items, method) {
     var values = [];
 
     // Steps 3-4.
     var i = 0;
     while (true) {
         // Step 4.a.
         var next = callContentFunction(iterator.next, iterator);
         if (!IsObject(next))
-            ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next");
+            ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
 
         // Step 4.b.
         if (next.done)
             break;
         _DefineDataProperty(values, i++, next.value);
     }
 
     // Step 5.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -53,17 +53,16 @@ using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::NumberIsInt32;
 using mozilla::PodCopy;
 using mozilla::Some;
 
 class BreakableControl;
 class LabelControl;
 class LoopControl;
-class ForOfLoopControl;
 class TryFinallyControl;
 
 static bool
 ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn)
 {
     return pn->getKind() == PNK_WHILE || pn->getKind() == PNK_FOR;
 }
 
@@ -148,23 +147,16 @@ template <>
 bool
 BytecodeEmitter::NestableControl::is<LoopControl>() const
 {
     return StatementKindIsLoop(kind_);
 }
 
 template <>
 bool
-BytecodeEmitter::NestableControl::is<ForOfLoopControl>() const
-{
-    return kind_ == StatementKind::ForOfLoop;
-}
-
-template <>
-bool
 BytecodeEmitter::NestableControl::is<TryFinallyControl>() const
 {
     return kind_ == StatementKind::Try || kind_ == StatementKind::Finally;
 }
 
 } // namespace frontend
 } // namespace js
 
@@ -273,74 +265,16 @@ class LoopControl : public BreakableCont
         MOZ_ASSERT(continueTarget.offset != -1);
         if (!patchBreaks(bce))
             return false;
         bce->patchJumpsToTarget(continues, continueTarget);
         return true;
     }
 };
 
-class ForOfLoopControl : public LoopControl
-{
-    // The stack depth of the iterator.
-    int32_t iterDepth_;
-
-    // for-of loops, when throwing from non-iterator code (i.e. from the body
-    // or from evaluating the LHS of the loop condition), need to call
-    // IteratorClose. If IteratorClose itself throws, we must not re-call
-    // IteratorClose. Since non-local jumps like break and return call
-    // IteratorClose, whenever a non-local jump is emitted, we must terminate
-    // the current JSTRY_ITERCLOSE note to skip the non-local jump code, then
-    // start a new one.
-    //
-    // Visually,
-    //
-    //   for (x of y) {
-    //     ...             instantiate ForOfLoopControl
-    //     ...         +   <-- iterCloseTryStart_ points to right before
-    //     ...                 assignment to loop variable
-    //     ...         ^
-    //     ...         |
-    //     if (...)    v
-    //                 +   call finishIterCloseTryNote before |break|
-    //                     above range is noted with JSTRY_ITERCLOSE
-    //
-    //       break;    <-- break and IteratorClose are not inside
-    //                     JSTRY_ITERCLOSE note
-    //
-    //                     call startNewIterCloseTryNote after |break|
-    //                 +   <-- next iterCloseTryStart_ points here
-    //     ...         |
-    //     ...         ~
-    //   }
-    ptrdiff_t iterCloseTryStart_;
-
-  public:
-    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth)
-      : LoopControl(bce, StatementKind::ForOfLoop),
-        iterDepth_(iterDepth),
-        iterCloseTryStart_(-1)
-    {
-        MOZ_ASSERT(bce->stackDepth >= iterDepth);
-    }
-
-    MOZ_MUST_USE bool finishIterCloseTryNote(BytecodeEmitter* bce) {
-        ptrdiff_t end = bce->offset();
-        MOZ_ASSERT(end >= iterCloseTryStart_);
-        if (end != iterCloseTryStart_)
-            return bce->tryNoteList.append(JSTRY_ITERCLOSE, iterDepth_, iterCloseTryStart_, end);
-        return true;
-    }
-
-    void startNewIterCloseTryNote(BytecodeEmitter* bce) {
-        MOZ_ASSERT(bce->offset() > iterCloseTryStart_);
-        iterCloseTryStart_ = bce->offset();
-    }
-};
-
 class TryFinallyControl : public BytecodeEmitter::NestableControl
 {
     bool emittingSubroutine_;
 
   public:
     // The subroutine when emitting a finally block.
     JumpList gosubs;
 
@@ -1564,166 +1498,16 @@ BytecodeEmitter::TDZCheckCache::noteTDZC
     } else {
         if (!cache_->add(p, name, check))
             return false;
     }
 
     return true;
 }
 
-class MOZ_STACK_CLASS IfThenElseEmitter
-{
-    BytecodeEmitter* bce_;
-    JumpList jumpAroundThen_;
-    JumpList jumpsAroundElse_;
-    unsigned noteIndex_;
-    int32_t thenDepth_;
-#ifdef DEBUG
-    int32_t pushed_;
-    bool calculatedPushed_;
-#endif
-    enum State {
-        Start,
-        If,
-        Cond,
-        IfElse,
-        Else,
-        End
-    };
-    State state_;
-
-  public:
-    explicit IfThenElseEmitter(BytecodeEmitter* bce)
-      : bce_(bce),
-        noteIndex_(-1),
-        thenDepth_(0),
-#ifdef DEBUG
-        pushed_(0),
-        calculatedPushed_(false),
-#endif
-        state_(Start)
-    {}
-
-    ~IfThenElseEmitter()
-    {}
-
-  private:
-    bool emitIf(State nextState) {
-        MOZ_ASSERT(state_ == Start || state_ == Else);
-        MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond);
-
-        // Clear jumpAroundThen_ offset that points previous JSOP_IFEQ.
-        if (state_ == Else)
-            jumpAroundThen_ = JumpList();
-
-        // Emit an annotated branch-if-false around the then part.
-        SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND;
-        if (!bce_->newSrcNote(type, &noteIndex_))
-            return false;
-        if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
-            return false;
-
-        // To restore stack depth in else part, save depth of the then part.
-#ifdef DEBUG
-        // If DEBUG, this is also necessary to calculate |pushed_|.
-        thenDepth_ = bce_->stackDepth;
-#else
-        if (nextState == IfElse || nextState == Cond)
-            thenDepth_ = bce_->stackDepth;
-#endif
-        state_ = nextState;
-        return true;
-    }
-
-  public:
-    bool emitIf() {
-        return emitIf(If);
-    }
-
-    bool emitCond() {
-        return emitIf(Cond);
-    }
-
-    bool emitIfElse() {
-        return emitIf(IfElse);
-    }
-
-    bool emitElse() {
-        MOZ_ASSERT(state_ == IfElse || state_ == Cond);
-
-        calculateOrCheckPushed();
-
-        // Emit a jump from the end of our then part around the else part. The
-        // patchJumpsToTarget call at the bottom of this function will fix up
-        // the offset with jumpsAroundElse value.
-        if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
-            return false;
-
-        // Ensure the branch-if-false comes here, then emit the else.
-        if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
-            return false;
-
-        // Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
-        // jump, for IonMonkey's benefit.  We can't just "back up" from the pc
-        // of the else clause, because we don't know whether an extended
-        // jump was required to leap from the end of the then clause over
-        // the else clause.
-        if (!bce_->setSrcNoteOffset(noteIndex_, 0,
-                                    jumpsAroundElse_.offset - jumpAroundThen_.offset))
-        {
-            return false;
-        }
-
-        // Restore stack depth of the then part.
-        bce_->stackDepth = thenDepth_;
-        state_ = Else;
-        return true;
-    }
-
-    bool emitEnd() {
-        MOZ_ASSERT(state_ == If || state_ == Else);
-
-        calculateOrCheckPushed();
-
-        if (state_ == If) {
-            // No else part, fixup the branch-if-false to come here.
-            if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
-                return false;
-        }
-
-        // Patch all the jumps around else parts.
-        if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
-            return false;
-
-        state_ = End;
-        return true;
-    }
-
-    void calculateOrCheckPushed() {
-#ifdef DEBUG
-        if (!calculatedPushed_) {
-            pushed_ = bce_->stackDepth - thenDepth_;
-            calculatedPushed_ = true;
-        } else {
-            MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
-        }
-#endif
-    }
-
-#ifdef DEBUG
-    int32_t pushed() const {
-        return pushed_;
-    }
-
-    int32_t popped() const {
-        return -pushed_;
-    }
-#endif
-};
-
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                  Parser<FullParseHandler>* parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
@@ -2230,53 +2014,32 @@ BytecodeEmitter::flushPops(int* npops)
         return false;
 
     *npops = 0;
     return true;
 }
 
 namespace {
 
-class NonLocalExitControl
-{
-  public:
-    enum Kind
-    {
-        // IteratorClose is handled especially inside the exception unwinder.
-        Throw,
-
-        // A 'continue' statement does not call IteratorClose for the loop it
-        // is continuing, i.e. excluding the target loop.
-        Continue,
-
-        // A 'break' or 'return' statement does call IteratorClose for the
-        // loop it is breaking out of or returning from, i.e. including the
-        // target loop.
-        Break,
-        Return
-    };
-
-  private:
+class NonLocalExitControl {
     BytecodeEmitter* bce_;
     const uint32_t savedScopeNoteIndex_;
     const int savedDepth_;
     uint32_t openScopeNoteIndex_;
-    Kind kind_;
 
     NonLocalExitControl(const NonLocalExitControl&) = delete;
 
     MOZ_MUST_USE bool leaveScope(BytecodeEmitter::EmitterScope* scope);
 
   public:
-    NonLocalExitControl(BytecodeEmitter* bce, Kind kind)
+    explicit NonLocalExitControl(BytecodeEmitter* bce)
       : bce_(bce),
         savedScopeNoteIndex_(bce->scopeNoteList.length()),
         savedDepth_(bce->stackDepth),
-        openScopeNoteIndex_(bce->innermostEmitterScope->noteIndex()),
-        kind_(kind)
+        openScopeNoteIndex_(bce->innermostEmitterScope->noteIndex())
     { }
 
     ~NonLocalExitControl() {
         for (uint32_t n = savedScopeNoteIndex_; n < bce_->scopeNoteList.length(); n++)
             bce_->scopeNoteList.recordEnd(n, bce_->offset(), bce_->inPrologue());
         bce_->stackDepth = savedDepth_;
     }
 
@@ -2313,24 +2076,16 @@ NonLocalExitControl::leaveScope(Bytecode
 bool
 NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* target)
 {
     using NestableControl = BytecodeEmitter::NestableControl;
     using EmitterScope = BytecodeEmitter::EmitterScope;
 
     EmitterScope* es = bce_->innermostEmitterScope;
     int npops = 0;
-    bool hasForOfLoopsWithIteratorClose = false;
-
-    // IteratorClose is handled specially in the exception unwinder. For
-    // 'continue', 'break', and 'return' statements, emit IteratorClose
-    // bytecode inline. 'continue' statements do not call IteratorClose for
-    // the loop they are continuing.
-    bool emitIteratorClose = kind_ == Continue || kind_ == Break || kind_ == Return;
-    bool emitIteratorCloseAtTarget = emitIteratorClose && kind_ != Continue;
 
     auto flushPops = [&npops](BytecodeEmitter* bce) {
         if (npops && !bce->flushPops(&npops))
             return false;
         return true;
     };
 
     // Walk the nestable control stack and patch jumps.
@@ -2360,96 +2115,48 @@ NonLocalExitControl::prepareForNonLocalJ
                     return false;
                 if (!bce_->emitJump(JSOP_GOSUB, &finallyControl.gosubs))
                     return false;
             }
             break;
           }
 
           case StatementKind::ForOfLoop:
-            // The iterator and the current value are on the stack.
-            //
-            if (emitIteratorClose) {
-                hasForOfLoopsWithIteratorClose = true;
-                if (!control->as<ForOfLoopControl>().finishIterCloseTryNote(bce_))
-                    return false;
-                if (!bce_->emit1(JSOP_POP))               // ... ITER
-                    return false;
-                if (!bce_->emitIteratorClose())           // ...
-                    return false;
-            } else {
-                if (!bce_->emit1(JSOP_POP))               // ... ITER
-                    return false;
-                if (!bce_->emit1(JSOP_POP))               // ...
-                    return false;
-            }
+            npops += 2;
             break;
 
           case StatementKind::ForInLoop:
-            // The iterator and the current value are on the stack.
-            if (!bce_->emit1(JSOP_POP))                   // ... ITER
-                return false;
-            if (!bce_->emit1(JSOP_ENDITER))               // ...
+            /* The iterator and the current value are on the stack. */
+            npops += 1;
+            if (!flushPops(bce_))
+                return false;
+            if (!bce_->emit1(JSOP_ENDITER))
                 return false;
             break;
 
           default:
             break;
         }
     }
 
-    if (target && target->is<ForOfLoopControl>() && emitIteratorCloseAtTarget) {
-        hasForOfLoopsWithIteratorClose = true;
-        if (!target->as<ForOfLoopControl>().finishIterCloseTryNote(bce_))
-            return false;
-
-        // The iterator and the current value are on the stack. At the level
-        // of the target block, there's bytecode after the loop that will pop
-        // the iterator and the value, so duplicate the iterator and call
-        // IteratorClose.
-        if (!bce_->emitDupAt(1))                          // ... ITER RESULT ITER
-            return false;
-        if (!bce_->emitIteratorClose())                   // ... ITER RESULT
-            return false;
-    }
-
     EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope;
     for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
         if (!leaveScope(es))
             return false;
     }
 
-    if (!flushPops(bce_))
-        return false;
-
-    // See comment in ForOfLoopControl.
-    if (hasForOfLoopsWithIteratorClose) {
-        for (NestableControl* control = bce_->innermostNestableControl;
-             control != target;
-             control = control->enclosing())
-        {
-            if (control->is<ForOfLoopControl>())
-                control->as<ForOfLoopControl>().startNewIterCloseTryNote(bce_);
-        }
-
-        if (target && target->is<ForOfLoopControl>() && emitIteratorCloseAtTarget)
-            target->as<ForOfLoopControl>().startNewIterCloseTryNote(bce_);
-    }
-
-    return true;
+    return flushPops(bce_);
 }
 
 }  // anonymous namespace
 
 bool
 BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist, SrcNoteType noteType)
 {
-    NonLocalExitControl nle(this, noteType == SRC_CONTINUE
-                                  ? NonLocalExitControl::Continue
-                                  : NonLocalExitControl::Break);
+    NonLocalExitControl nle(this);
 
     if (!nle.prepareForNonLocalJump(target))
         return false;
 
     if (noteType != SRC_NULL) {
         if (!newSrcNote(noteType))
             return false;
     }
@@ -4826,154 +4533,16 @@ BytecodeEmitter::emitIteratorNext(ParseN
         return false;
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
         return false;
     checkTypeSet(JSOP_CALL);
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorClose(Maybe<JumpTarget> yieldStarTryStart, bool allowSelfHosted)
-{
-    MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
-               ".close() on iterators is prohibited in self-hosted code because it "
-               "can run user-modifiable iteration code");
-
-    // Generate inline logic corresponding to IteratorClose (ES 7.4.6).
-    //
-    // Callers need to ensure that the iterator object is at the top of the
-    // stack.
-
-    if (!emit1(JSOP_DUP))                                 // ... ITER ITER
-        return false;
-
-    // Step 3.
-    //
-    // Get the "return" method.
-    if (!emitAtomOp(cx->names().return_, JSOP_CALLPROP))  // ... ITER RET
-        return false;
-
-    // Step 4.
-    //
-    // Do nothing if "return" is null or undefined.
-    IfThenElseEmitter ifReturnMethodIsDefined(this);
-    if (!emit1(JSOP_DUP))                                 // ... ITER RET RET
-        return false;
-    if (!emit1(JSOP_UNDEFINED))                           // ... ITER RET RET UNDEFINED
-        return false;
-    if (!emit1(JSOP_NE))                                  // ... ITER RET ?NEQL
-        return false;
-    if (!ifReturnMethodIsDefined.emitIfElse())
-        return false;
-
-    // Steps 5, 8.
-    //
-    // Call "return" if it is not undefined or null, and check that it returns
-    // an Object.
-    if (!emit1(JSOP_SWAP))                                // ... RET ITER
-        return false;
-
-    // ES 14.4.13, yield * AssignmentExpression, step 5.c
-    //
-    // When emitting iterator.return() for yield* forced return, we need to
-    // pass the argument passed to Generator.prototype.return to the return
-    // method.
-    if (yieldStarTryStart) {
-        IfThenElseEmitter ifGeneratorClosing(this);
-        if (!emitDupAt(2))                                // ... FTYPE FVALUE RET ITER FVALUE
-            return false;
-        if (!emit1(JSOP_ISGENCLOSING))                    // ... FTYPE FVALUE RET ITER FVALUE CLOSING
-            return false;
-        if (!emit1(JSOP_SWAP))                            // ... FTYPE FVALUE RET ITER CLOSING FVALUE
-            return false;
-        if (!emit1(JSOP_POP))                             // ... FTYPE FVALUE RET ITER CLOSING
-            return false;
-        if (!ifGeneratorClosing.emitIfElse())             // ... FTYPE FVALUE RET ITER
-            return false;
-
-        if (!emit1(JSOP_GETRVAL))                         // ... FTYPE FVALUE RET ITER RVAL
-            return false;
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... FTYPE FVALUE RET ITER VALUE
-            return false;
-        if (!emitCall(JSOP_CALL, 1))                      // ... FTYPE FVALUE RESULT
-            return false;
-        checkTypeSet(JSOP_CALL);
-        if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... FTYPE FVALUE RESULT
-            return false;
-
-        IfThenElseEmitter ifReturnDone(this);
-        if (!emit1(JSOP_DUP))                             // ITER OLDRESULT FTYPE FVALUE RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER OLDRESULT FTYPE FVALUE RESULT DONE
-            return false;
-        if (!ifReturnDone.emitIfElse())                   // ITER OLDRESULT FTYPE FVALUE RESULT
-            return false;
-        if (!emit1(JSOP_DUP))                             // ITER OLDRESULT FTYPE FVALUE RESULT RESULT
-            return false;
-        if (!emit1(JSOP_SETRVAL))                         // ITER OLDRESULT FTYPE FVALUE RESULT
-            return false;
-        if (!ifReturnDone.emitElse())                     // ITER OLDRESULT FTYPE FVALUE RESULT
-            return false;
-        int32_t savedDepth = this->stackDepth;
-        if (!emit2(JSOP_UNPICK, 3))                       // ITER RESULT OLDRESULT FTYPE FVALUE
-            return false;
-        if (!emitUint16Operand(JSOP_POPN, 3))             // ITER RESULT
-            return false;
-        JumpList beq;
-        JumpTarget breakTarget{ -1 };
-        if (!emitBackwardJump(JSOP_GOTO, *yieldStarTryStart, &beq, &breakTarget)) // ITER RESULT
-            return false;
-        this->stackDepth = savedDepth;
-        if (!ifReturnDone.emitEnd())
-            return false;
-
-        if (!ifGeneratorClosing.emitElse())               // ... FTYPE FVALUE RET ITER
-            return false;
-        if (!emitCall(JSOP_CALL, 0))                      // ... FTYPE FVALUE RESULT
-            return false;
-        checkTypeSet(JSOP_CALL);
-        if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... FTYPE FVALUE RESULT
-            return false;
-
-        if (!ifGeneratorClosing.emitEnd())
-            return false;
-    } else {
-        if (!emitCall(JSOP_CALL, 0))                      // ... RESULT
-            return false;
-        checkTypeSet(JSOP_CALL);
-        if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RESULT
-            return false;
-    }
-
-    if (!ifReturnMethodIsDefined.emitElse())
-        return false;
-    if (!emit1(JSOP_POP))                                 // ... ITER
-        return false;
-    if (!ifReturnMethodIsDefined.emitEnd())
-        return false;
-
-    return emit1(JSOP_POP);                               // ...
-}
-
-template <typename InnerEmitter>
-bool
-BytecodeEmitter::wrapWithIteratorCloseTryNote(int32_t iterDepth, InnerEmitter emitter)
-{
-    MOZ_ASSERT(this->stackDepth >= iterDepth);
-
-    ptrdiff_t start = offset();
-    if (!emitter(this))
-        return false;
-    ptrdiff_t end = offset();
-    if (start != end)
-        return tryNoteList.append(JSTRY_ITERCLOSE, iterDepth, start, end);
-    return true;
-}
-
-bool
 BytecodeEmitter::emitDefault(ParseNode* defaultExpr, ParseNode* pattern)
 {
     if (!emit1(JSOP_DUP))                                 // VALUE VALUE
         return false;
     if (!emit1(JSOP_UNDEFINED))                           // VALUE VALUE UNDEFINED
         return false;
     if (!emit1(JSOP_STRICTEQ))                            // VALUE EQL?
         return false;
@@ -5051,16 +4620,166 @@ BytecodeEmitter::emitInitializer(ParseNo
 
 bool
 BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern)
 {
     TDZCheckCache tdzCache(this);
     return emitInitializer(initializer, pattern);
 }
 
+class MOZ_STACK_CLASS IfThenElseEmitter
+{
+    BytecodeEmitter* bce_;
+    JumpList jumpAroundThen_;
+    JumpList jumpsAroundElse_;
+    unsigned noteIndex_;
+    int32_t thenDepth_;
+#ifdef DEBUG
+    int32_t pushed_;
+    bool calculatedPushed_;
+#endif
+    enum State {
+        Start,
+        If,
+        Cond,
+        IfElse,
+        Else,
+        End
+    };
+    State state_;
+
+  public:
+    explicit IfThenElseEmitter(BytecodeEmitter* bce)
+      : bce_(bce),
+        noteIndex_(-1),
+        thenDepth_(0),
+#ifdef DEBUG
+        pushed_(0),
+        calculatedPushed_(false),
+#endif
+        state_(Start)
+    {}
+
+    ~IfThenElseEmitter()
+    {}
+
+  private:
+    bool emitIf(State nextState) {
+        MOZ_ASSERT(state_ == Start || state_ == Else);
+        MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond);
+
+        // Clear jumpAroundThen_ offset that points previous JSOP_IFEQ.
+        if (state_ == Else)
+            jumpAroundThen_ = JumpList();
+
+        // Emit an annotated branch-if-false around the then part.
+        SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND;
+        if (!bce_->newSrcNote(type, &noteIndex_))
+            return false;
+        if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
+            return false;
+
+        // To restore stack depth in else part, save depth of the then part.
+#ifdef DEBUG
+        // If DEBUG, this is also necessary to calculate |pushed_|.
+        thenDepth_ = bce_->stackDepth;
+#else
+        if (nextState == IfElse || nextState == Cond)
+            thenDepth_ = bce_->stackDepth;
+#endif
+        state_ = nextState;
+        return true;
+    }
+
+  public:
+    bool emitIf() {
+        return emitIf(If);
+    }
+
+    bool emitCond() {
+        return emitIf(Cond);
+    }
+
+    bool emitIfElse() {
+        return emitIf(IfElse);
+    }
+
+    bool emitElse() {
+        MOZ_ASSERT(state_ == IfElse || state_ == Cond);
+
+        calculateOrCheckPushed();
+
+        // Emit a jump from the end of our then part around the else part. The
+        // patchJumpsToTarget call at the bottom of this function will fix up
+        // the offset with jumpsAroundElse value.
+        if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
+            return false;
+
+        // Ensure the branch-if-false comes here, then emit the else.
+        if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
+            return false;
+
+        // Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
+        // jump, for IonMonkey's benefit.  We can't just "back up" from the pc
+        // of the else clause, because we don't know whether an extended
+        // jump was required to leap from the end of the then clause over
+        // the else clause.
+        if (!bce_->setSrcNoteOffset(noteIndex_, 0,
+                                    jumpsAroundElse_.offset - jumpAroundThen_.offset))
+        {
+            return false;
+        }
+
+        // Restore stack depth of the then part.
+        bce_->stackDepth = thenDepth_;
+        state_ = Else;
+        return true;
+    }
+
+    bool emitEnd() {
+        MOZ_ASSERT(state_ == If || state_ == Else);
+
+        calculateOrCheckPushed();
+
+        if (state_ == If) {
+            // No else part, fixup the branch-if-false to come here.
+            if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
+                return false;
+        }
+
+        // Patch all the jumps around else parts.
+        if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
+            return false;
+
+        state_ = End;
+        return true;
+    }
+
+    void calculateOrCheckPushed() {
+#ifdef DEBUG
+        if (!calculatedPushed_) {
+            pushed_ = bce_->stackDepth - thenDepth_;
+            calculatedPushed_ = true;
+        } else {
+            MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
+        }
+#endif
+    }
+
+#ifdef DEBUG
+    int32_t pushed() const {
+        return pushed_;
+    }
+
+    int32_t popped() const {
+        return -pushed_;
+    }
+#endif
+};
+
 bool
 BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav)
 {
     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;|
@@ -5994,17 +5713,17 @@ BytecodeEmitter::emitCatch(ParseNode* pn
         // If the guard expression is false, fall through, pop the block scope,
         // and jump to the next catch block.  Otherwise jump over that code and
         // pop the dupped exception.
         JumpList guardCheck;
         if (!emitJump(JSOP_IFNE, &guardCheck))
             return false;
 
         {
-            NonLocalExitControl nle(this, NonLocalExitControl::Throw);
+            NonLocalExitControl nle(this);
 
             // Move exception back to cx->exception to prepare for
             // the next catch.
             if (!emit1(JSOP_THROWING))
                 return false;
 
             // Leave the scope for this catch block.
             if (!nle.prepareForNonLocalJump(&controlInfo))
@@ -6587,24 +6306,22 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 
     // Evaluate the expression being iterated.
     ParseNode* forHeadExpr = forOfHead->pn_kid3;
     if (!emitTree(forHeadExpr))                           // ITERABLE
         return false;
     if (!emitIterator())                                  // ITER
         return false;
 
-    int32_t iterDepth = stackDepth;
-
     // For-of loops have both the iterator and the value on the stack. Push
     // undefined to balance the stack.
     if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT
         return false;
 
-    ForOfLoopControl loopInfo(this, iterDepth);
+    LoopControl loopInfo(this, StatementKind::ForOfLoop);
 
     // Annotate so IonMonkey can find the loop-closing jump.
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     JumpList initialJump;
     if (!emitJump(JSOP_GOTO, &initialJump))               // ITER RESULT
@@ -6640,44 +6357,36 @@ BytecodeEmitter::emitForOf(ParseNode* fo
     JumpList beq;
     JumpTarget breakTarget{ -1 };
     {
 #ifdef DEBUG
         auto loopDepth = this->stackDepth;
 #endif
 
         // Emit code to assign result.value to the iteration variable.
-        //
-        // Note that ES 13.7.5.13, step 5.c says getting result.value does not
-        // call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP.
         if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
             return false;
         if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
             return false;
 
-        loopInfo.startNewIterCloseTryNote(this);
-
         if (!emitInitializeForInOrOfTarget(forOfHead))    // ITER RESULT VALUE
             return false;
 
         if (!emit1(JSOP_POP))                             // ITER RESULT
             return false;
 
-        MOZ_ASSERT(stackDepth == loopDepth,
+        MOZ_ASSERT(this->stackDepth == loopDepth,
                    "the stack must be balanced around the initializing "
                    "operation");
 
         // Perform the loop body.
         ParseNode* forBody = forOfLoop->pn_right;
         if (!emitTree(forBody))                           // ITER RESULT
             return false;
 
-        if (!loopInfo.finishIterCloseTryNote(this))
-            return false;
-
         // Set offset for continues.
         loopInfo.continueTarget = { offset() };
 
         if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER RESULT
             return false;
 
         if (!emit1(JSOP_POP))                             // ITER
             return false;
@@ -7903,17 +7612,17 @@ BytecodeEmitter::emitReturn(ParseNode* p
 
     // Make sure that we emit this before popping the blocks in prepareForNonLocalJump,
     // to ensure that the error is thrown while the scope-chain is still intact.
     if (isDerivedClassConstructor) {
         if (!emitCheckDerivedClassConstructorReturn())
             return false;
     }
 
-    NonLocalExitControl nle(this, NonLocalExitControl::Return);
+    NonLocalExitControl nle(this);
 
     if (!nle.prepareForNonLocalJumpToOutermost())
         return false;
 
     if (isGenerator) {
         // We know that .generator is on the function scope, as we just exited
         // all nested scopes.
         NameLocation loc =
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -674,22 +674,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     // emitIterator expects the iterable to already be on the stack.
     // It will replace that stack value with the corresponding iterator
     MOZ_MUST_USE bool emitIterator();
 
     // Pops iterator from the top of the stack. Pushes the result of |.next()|
     // onto the stack.
     MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
-    MOZ_MUST_USE bool emitIteratorClose(
-        mozilla::Maybe<JumpTarget> yieldStarTryStart = mozilla::Nothing(),
-        bool allowSelfHosted = false);
-
-    template <typename InnerEmitter>
-    MOZ_MUST_USE bool wrapWithIteratorCloseTryNote(int32_t iterDepth, InnerEmitter emitter);
 
     // Check if the value on top of the stack is "undefined". If so, replace
     // that value on the stack with the value defined by |defaultExpr|.
     // |pattern| is a lhs node of the default expression.  If it's an
     // identifier and |defaultExpr| is an anonymous function, |SetFunctionName|
     // is called at compile time.
     MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -323,41 +323,33 @@ JitFrameIterator::machineState() const
 static uint32_t
 NumArgAndLocalSlots(const InlineFrameIterator& frame)
 {
     JSScript* script = frame.script();
     return CountArgSlots(script, frame.maybeCalleeTemplate()) + script->nfixed();
 }
 
 static void
-CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, JSTryNote* tn)
+CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, uint32_t stackSlot)
 {
-    MOZ_ASSERT(tn->kind == JSTRY_FOR_IN || tn->kind == JSTRY_ITERCLOSE);
-    MOZ_ASSERT(tn->stackDepth > 0);
-
     SnapshotIterator si = frame.snapshotIterator();
 
     // Skip stack slots until we reach the iterator object.
-    uint32_t stackSlot = tn->stackDepth;
     uint32_t skipSlots = NumArgAndLocalSlots(frame) + stackSlot - 1;
 
     for (unsigned i = 0; i < skipSlots; i++)
         si.skip();
 
     Value v = si.read();
     RootedObject obj(cx, &v.toObject());
 
-    if (cx->isExceptionPending()) {
-        if (tn->kind == JSTRY_FOR_IN)
-            UnwindIteratorForException(cx, obj);
-        else
-            IteratorCloseForException(cx, obj);
-    } else {
+    if (cx->isExceptionPending())
+        UnwindIteratorForException(cx, obj);
+    else
         UnwindIteratorForUncatchableException(cx, obj);
-    }
 }
 
 class IonFrameStackDepthOp
 {
     uint32_t depth_;
 
   public:
     explicit IonFrameStackDepthOp(const InlineFrameIterator& frame) {
@@ -420,22 +412,22 @@ HandleExceptionIon(JSContext* cx, const 
     RootedScript script(cx, frame.script());
     if (!script->hasTrynotes())
         return;
 
     for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
         JSTryNote* tn = *tni;
 
         switch (tn->kind) {
-          case JSTRY_FOR_IN:
-          case JSTRY_ITERCLOSE: {
-            MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
-                          JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
+          case JSTRY_FOR_IN: {
+            MOZ_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
             MOZ_ASSERT(tn->stackDepth > 0);
-            CloseLiveIteratorIon(cx, frame, tn);
+
+            uint32_t localSlot = tn->stackDepth;
+            CloseLiveIteratorIon(cx, frame, localSlot);
             break;
           }
 
           case JSTRY_FOR_OF:
           case JSTRY_LOOP:
             break;
 
           case JSTRY_CATCH:
@@ -601,33 +593,27 @@ ProcessTryNotesBaseline(JSContext* cx, c
             rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc);
             // Drop the exception instead of leaking cross compartment data.
             if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception)))
                 rfe->exception = UndefinedValue();
             cx->clearPendingException();
             return true;
           }
 
-          case JSTRY_FOR_IN:
-          case JSTRY_ITERCLOSE: {
+          case JSTRY_FOR_IN: {
             uint8_t* framePointer;
             uint8_t* stackPointer;
             BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
-            Value iterValue(*(reinterpret_cast<Value*>(stackPointer)));
+            Value iterValue(*(Value*) stackPointer);
             RootedObject iterObject(cx, &iterValue.toObject());
-            bool ok;
-            if (tn->kind == JSTRY_FOR_IN)
-                ok = UnwindIteratorForException(cx, iterObject);
-            else
-                ok = IteratorCloseForException(cx, iterObject);
-            if (!ok) {
+            if (!UnwindIteratorForException(cx, iterObject)) {
                 // See comment in the JSTRY_FOR_IN case in Interpreter.cpp's
                 // ProcessTryNotes.
                 SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
-                MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN, **pc == JSOP_ENDITER);
+                MOZ_ASSERT(**pc == JSOP_ENDITER);
                 return false;
             }
             break;
           }
 
           case JSTRY_FOR_OF:
           case JSTRY_LOOP:
             break;
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -90,17 +90,17 @@ MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH
 MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
 MSG_DEF(JSMSG_BAD_GET_SET_FIELD,       1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
 MSG_DEF(JSMSG_THROW_TYPE_ERROR,        0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
 MSG_DEF(JSMSG_NOT_EXPECTED_TYPE,       3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
 MSG_DEF(JSMSG_NOT_ITERABLE,            1, JSEXN_TYPEERR, "{0} is not iterable")
 MSG_DEF(JSMSG_NOT_ITERATOR,            1, JSEXN_TYPEERR, "{0} is not iterator")
 MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA,      2, JSEXN_WARN, "{0} is being assigned a {1}, but already has one")
 MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
-MSG_DEF(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, 1, JSEXN_TYPEERR, "iterator.{0}() returned a non-object value")
+MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value")
 MSG_DEF(JSMSG_CANT_SET_PROTO,          0, JSEXN_TYPEERR, "can't set prototype of this object")
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL,     1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
 MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -7,17 +7,16 @@
 /* JavaScript iterators. */
 
 #include "jsiter.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
-#include "mozilla/Unused.h"
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscript.h"
@@ -1213,17 +1212,16 @@ js::CloseIterator(JSContext* cx, HandleO
         if (genObj->isClosed())
             return true;
         if (genObj->isRunning() || genObj->isClosing()) {
             // Nothing sensible to do.
             return true;
         }
         return LegacyGeneratorObject::close(cx, obj);
     }
-
     return true;
 }
 
 bool
 js::UnwindIteratorForException(JSContext* cx, HandleObject obj)
 {
     RootedValue v(cx);
     bool getOk = cx->getPendingException(&v);
@@ -1231,66 +1229,16 @@ js::UnwindIteratorForException(JSContext
     if (!CloseIterator(cx, obj))
         return false;
     if (!getOk)
         return false;
     cx->setPendingException(v);
     return true;
 }
 
-bool
-js::IteratorCloseForException(JSContext* cx, HandleObject obj)
-{
-    MOZ_ASSERT(cx->isExceptionPending());
-
-    bool isClosingGenerator = cx->isClosingGenerator();
-    JS::AutoSaveExceptionState savedExc(cx);
-
-    // Implements IteratorClose (ES 7.4.6) for exception unwinding. See
-    // also the bytecode generated by BytecodeEmitter::emitIteratorClose.
-
-    // Step 3.
-    //
-    // Get the "return" method.
-    RootedValue returnMethod(cx);
-    if (!GetProperty(cx, obj, obj, cx->names().return_, &returnMethod))
-        return false;
-
-    // Step 4.
-    //
-    // Do nothing if "return" is null or undefined. Throw a TypeError if the
-    // method is not IsCallable.
-    if (returnMethod.isNullOrUndefined())
-        return true;
-    if (!IsCallable(returnMethod))
-        return ReportIsNotFunction(cx, returnMethod);
-
-    // Step 5, 6, 8.
-    //
-    // Call "return" if it is not null or undefined.
-    RootedValue rval(cx);
-    bool ok = Call(cx, returnMethod, obj, &rval);
-    if (isClosingGenerator) {
-        // Closing an iterator is implemented as an exception, but in spec
-        // terms it is a Completion value with [[Type]] return. In this case
-        // we *do* care if the call threw and if it returned an object.
-        if (!ok)
-            return false;
-        if (!rval.isObject())
-            return ThrowCheckIsObject(cx, CheckIsObjectKind::IteratorReturn);
-    } else {
-        // We don't care if the call threw or that it returned an Object, as
-        // Step 6 says if IteratorClose is being called during a throw, the
-        // original throw has primacy.
-        savedExc.restore();
-    }
-
-    return true;
-}
-
 void
 js::UnwindIteratorForUncatchableException(JSContext* cx, JSObject* obj)
 {
     if (obj->is<PropertyIteratorObject>()) {
         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
         if (ni->flags & JSITER_ENUMERATE)
             ni->unlink();
     }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -178,19 +178,16 @@ JSObject*
 ValueToIterator(JSContext* cx, unsigned flags, HandleValue vp);
 
 bool
 CloseIterator(JSContext* cx, HandleObject iterObj);
 
 bool
 UnwindIteratorForException(JSContext* cx, HandleObject obj);
 
-bool
-IteratorCloseForException(JSContext* cx, HandleObject obj);
-
 void
 UnwindIteratorForUncatchableException(JSContext* cx, JSObject* obj);
 
 bool
 IteratorConstructor(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -79,18 +79,17 @@ CopyScript(JSContext* cx, HandleScript s
  * for exception unwinding, but storing their boundaries here is helpful for
  * heuristics that need to know whether a given op is inside a loop.
  */
 enum JSTryNoteKind {
     JSTRY_CATCH,
     JSTRY_FINALLY,
     JSTRY_FOR_IN,
     JSTRY_FOR_OF,
-    JSTRY_LOOP,
-    JSTRY_ITERCLOSE
+    JSTRY_LOOP
 };
 
 /*
  * Exception handling record.
  */
 struct JSTryNote {
     uint8_t         kind;       /* one of JSTryNoteKind */
     uint32_t        stackDepth; /* stack depth upon exception handler entry */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2588,34 +2588,33 @@ Notes(JSContext* cx, unsigned argc, Valu
     args.rval().setString(str);
     return true;
 }
 
 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
 JS_STATIC_ASSERT(JSTRY_FOR_IN == 2);
 
-static const char* const TryNoteNames[] = { "catch", "finally", "for-in", "for-of", "loop",
-                                            "iterclose" };
+static const char* const TryNoteNames[] = { "catch", "finally", "for-in", "for-of", "loop" };
 
 static MOZ_MUST_USE bool
 TryNotes(JSContext* cx, HandleScript script, Sprinter* sp)
 {
     if (!script->hasTrynotes())
         return true;
 
-    if (sp->put("\nException table:\nkind        stack    start      end\n") < 0)
+    if (sp->put("\nException table:\nkind      stack    start      end\n") < 0)
         return false;
 
     JSTryNote* tn = script->trynotes()->vector;
     JSTryNote* tnlimit = tn + script->trynotes()->length;
     do {
         MOZ_ASSERT(tn->kind < ArrayLength(TryNoteNames));
-        uint32_t startOff = script->pcToOffset(script->main()) + tn->start;
-        if (!sp->jsprintf(" %-9s %6u %8u %8u\n",
+        uint8_t startOff = script->pcToOffset(script->main()) + tn->start;
+        if (!sp->jsprintf(" %-7s %6u %8u %8u\n",
                           TryNoteNames[tn->kind], tn->stackDepth,
                           startOff, startOff + tn->length))
         {
             return false;
         }
     } while (++tn != tnlimit);
     return true;
 }
deleted file mode 100644
--- a/js/src/tests/ecma_6/Statements/for-of-iterator-close.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// Tests that IteratorReturn is called when a for-of loop has an abrupt
-// completion value during non-iterator code.
-
-function test() {
-    var returnCalled = 0;
-    var returnCalledExpected = 0;
-    var iterable = {};
-    iterable[Symbol.iterator] = makeIterator({
-        ret: function() {
-            returnCalled++;
-            return {};
-        }
-    });
-
-    // break calls iter.return
-    for (var x of iterable)
-        break;
-    assertEq(returnCalled, ++returnCalledExpected);
-
-    // throw in body calls iter.return
-    assertThrowsValue(function() {
-        for (var x of iterable)
-            throw "in body";
-    }, "in body");
-    assertEq(returnCalled, ++returnCalledExpected);
-
-    // throw in lhs ref calls iter.return
-    function throwlhs() {
-        throw "in lhs";
-    }
-    assertThrowsValue(function() {
-        for ((throwlhs()) of iterable)
-            continue;
-    }, "in lhs");
-    assertEq(returnCalled, ++returnCalledExpected);
-
-    // throw in iter.return doesn't re-call iter.return
-    iterable[Symbol.iterator] = makeIterator({
-        ret: function() {
-            returnCalled++;
-            throw "in iter.return";
-        }
-    });
-    assertThrowsValue(function() {
-        for (var x of iterable)
-            break;
-    }, "in iter.return");
-    assertEq(returnCalled, ++returnCalledExpected);
-
-    // throw in iter.next doesn't call IteratorClose
-    iterable[Symbol.iterator] = makeIterator({
-        next: function() {
-            throw "in next";
-        }
-    });
-    assertThrowsValue(function() {
-        for (var x of iterable)
-            break;
-    }, "in next");
-    assertEq(returnCalled, returnCalledExpected);
-
-    // "return" must return an Object.
-    iterable[Symbol.iterator] = makeIterator({
-        ret: function() {
-            returnCalled++;
-            return 42;
-        }
-    });
-    assertThrowsInstanceOf(function() {
-        for (var x of iterable)
-            break;
-    }, TypeError);
-    assertEq(returnCalled, ++returnCalledExpected);
-
-    // continue doesn't call iter.return for the loop it's continuing
-    var i = 0;
-    iterable[Symbol.iterator] = makeIterator({
-        next: function() {
-            return { done: i++ > 5 };
-        },
-        ret: function() {
-            returnCalled++;
-            return {};
-        }
-    });
-    for (var x of iterable)
-        continue;
-    assertEq(returnCalled, returnCalledExpected);
-
-    // continue does call iter.return for loops it skips
-    i = 0;
-    L: do {
-        for (var x of iterable)
-            continue L;
-    } while (false);
-    assertEq(returnCalled, ++returnCalledExpected);
-}
-
-test();
-
-if (typeof reportCompare === "function")
-    reportCompare(0, 0);
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -1,46 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 (function(global) {
-    /** Yield every permutation of the elements in some array. */
-    global.Permutations = function* Permutations(items) {
-        if (items.length == 0) {
-            yield [];
-        } else {
-            items = items.slice(0);
-            for (let i = 0; i < items.length; i++) {
-                let swap = items[0];
-                items[0] = items[i];
-                items[i] = swap;
-                for (let e of Permutations(items.slice(1, items.length)))
-                    yield [items[0]].concat(e);
-            }
-        }
-    };
-
-    /** Make an iterator with a return method. */
-    global.makeIterator = function makeIterator(overrides) {
-        var iterator = {
-            next: function() {
-                if (overrides && overrides.next)
-                    return overrides.next();
-                return { done: false };
-            },
-            return: function() {
-                if (overrides && overrides.ret)
-                    return overrides.ret();
-                return { done: true };
-            }
-        };
-
-        return function() { return iterator; };
-    };
+  /** Yield every permutation of the elements in some array. */
+  global.Permutations = function* Permutations(items) {
+      if (items.length == 0) {
+          yield [];
+      } else {
+          items = items.slice(0);
+          for (let i = 0; i < items.length; i++) {
+              let swap = items[0];
+              items[0] = items[i];
+              items[i] = swap;
+              for (let e of Permutations(items.slice(1, items.length)))
+                  yield [items[0]].concat(e);
+          }
+      }
+  };
 })(this);
 
 if (typeof assertThrowsInstanceOf === 'undefined') {
     var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) {
         var fullmsg;
         try {
             f();
         } catch (exc) {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1169,29 +1169,23 @@ ProcessTryNotes(JSContext* cx, Environme
                 break;
             SettleOnTryNote(cx, tn, ei, regs);
             return CatchContinuation;
 
           case JSTRY_FINALLY:
             SettleOnTryNote(cx, tn, ei, regs);
             return FinallyContinuation;
 
-          case JSTRY_FOR_IN:
-          case JSTRY_ITERCLOSE: {
+          case JSTRY_FOR_IN: {
             /* This is similar to JSOP_ENDITER in the interpreter loop. */
             DebugOnly<jsbytecode*> pc = regs.fp()->script()->main() + tn->start + tn->length;
-            MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN, JSOp(*pc) == JSOP_ENDITER);
+            MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER);
             Value* sp = regs.spForStackDepth(tn->stackDepth);
             RootedObject obj(cx, &sp[-1].toObject());
-            bool ok;
-            if (tn->kind == JSTRY_FOR_IN)
-                ok = UnwindIteratorForException(cx, obj);
-            else
-                ok = IteratorCloseForException(cx, obj);
-            if (!ok) {
+            if (!UnwindIteratorForException(cx, obj)) {
                 // We should only settle on the note only if
                 // UnwindIteratorForException itself threw, as
                 // onExceptionUnwind should be called anew with the new
                 // location of the throw (the iterator). Indeed, we must
                 // settle to avoid infinitely handling the same exception.
                 SettleOnTryNote(cx, tn, ei, regs);
                 return ErrorReturnContinuation;
             }
@@ -5043,22 +5037,17 @@ js::ReportRuntimeRedeclaration(JSContext
     }
 }
 
 bool
 js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
 {
     switch (kind) {
       case CheckIsObjectKind::IteratorNext:
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                  JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next");
-        break;
-      case CheckIsObjectKind::IteratorReturn:
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                  JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "return");
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEXT_RETURNED_PRIMITIVE);
         break;
       case CheckIsObjectKind::GetIterator:
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
         break;
       default:
         MOZ_CRASH("Unknown kind");
     }
     return false;
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -557,17 +557,16 @@ ReportRuntimeLexicalError(JSContext* cx,
 // The parser only reports redeclarations that occurs within a single
 // script. Due to the extensibility of the global lexical scope, we also check
 // for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}.
 void
 ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind);
 
 enum class CheckIsObjectKind : uint8_t {
     IteratorNext,
-    IteratorReturn,
     GetIterator
 };
 
 bool
 ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
 
 bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);