Backed out changeset 7b8f0863a844
authorDavid Anderson <danderson@mozilla.com>
Mon, 28 Jan 2013 13:55:48 -0800
changeset 127339 b9492e41fc38c6a13d777cf5e887ce71a09655eb
parent 127338 7b8f0863a84464791b6ce04a7530162c9881ff8a
child 127340 08cf71e048a7dc23313c45236c015381491128ec
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a2
backs out7b8f0863a84464791b6ce04a7530162c9881ff8a
Backed out changeset 7b8f0863a844
js/src/ion/CodeGenerator.cpp
js/src/ion/Lowering.cpp
js/src/jscntxt.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsiter.h
js/src/methodjit/Compiler.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -3375,31 +3375,20 @@ CodeGenerator::visitIteratorStart(LItera
         masm.bind(&noBarrier);
     }
 
     // Mark iterator as active.
     masm.storePtr(obj, Address(niTemp, offsetof(NativeIterator, obj)));
     masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags)));
 
     // Chain onto the active iterator stack.
-    masm.movePtr(ImmWord(GetIonContext()->compartment), temp1);
-    masm.loadPtr(Address(temp1, offsetof(JSCompartment, enumerators)), temp1);
-
-    // ni->next = list
-    masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext()));
-
-    // ni->prev = list->prev
-    masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2);
-    masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev()));
-
-    // list->prev->next = ni
-    masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext()));
-
-    // list->prev = ni
-    masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev()));
+    masm.loadJSContext(temp1);
+    masm.loadPtr(Address(temp1, offsetof(JSContext, enumerators)), temp2);
+    masm.storePtr(temp2, Address(niTemp, offsetof(NativeIterator, next)));
+    masm.storePtr(output, Address(temp1, offsetof(JSContext, enumerators)));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 static void
 LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *failures)
 {
@@ -3476,17 +3465,16 @@ typedef bool (*CloseIteratorFn)(JSContex
 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
 
 bool
 CodeGenerator::visitIteratorEnd(LIteratorEnd *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register temp1 = ToRegister(lir->temp1());
     const Register temp2 = ToRegister(lir->temp2());
-    const Register temp3 = ToRegister(lir->temp3());
 
     OutOfLineCode *ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing());
     if (!ool)
         return false;
 
     LoadNativeIterator(masm, obj, temp1, ool->entry());
 
     masm.branchTest32(Assembler::Zero, Address(temp1, offsetof(NativeIterator, flags)),
@@ -3494,27 +3482,20 @@ CodeGenerator::visitIteratorEnd(LIterato
 
     // Clear active bit.
     masm.and32(Imm32(~JSITER_ACTIVE), Address(temp1, offsetof(NativeIterator, flags)));
 
     // Reset property cursor.
     masm.loadPtr(Address(temp1, offsetof(NativeIterator, props_array)), temp2);
     masm.storePtr(temp2, Address(temp1, offsetof(NativeIterator, props_cursor)));
 
-    // Unlink from the iterator list.
-    const Register next = temp2;
-    const Register prev = temp3;
-    masm.loadPtr(Address(temp1, NativeIterator::offsetOfNext()), next);
-    masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), prev);
-    masm.storePtr(prev, Address(next, NativeIterator::offsetOfPrev()));
-    masm.storePtr(next, Address(prev, NativeIterator::offsetOfNext()));
-#ifdef DEBUG
-    masm.storePtr(ImmWord(uintptr_t(0)), Address(temp1, NativeIterator::offsetOfNext()));
-    masm.storePtr(ImmWord(uintptr_t(0)), Address(temp1, NativeIterator::offsetOfPrev()));
-#endif
+    // Advance enumerators list.
+    masm.loadJSContext(temp2);
+    masm.loadPtr(Address(temp1, offsetof(NativeIterator, next)), temp1);
+    masm.storePtr(temp1, Address(temp2, offsetof(JSContext, enumerators)));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitArgumentsLength(LArgumentsLength *lir)
 {
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -2049,17 +2049,17 @@ LIRGenerator::visitIteratorMore(MIterato
 {
     LIteratorMore *lir = new LIteratorMore(useRegister(ins->iterator()), temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitIteratorEnd(MIteratorEnd *ins)
 {
-    LIteratorEnd *lir = new LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp());
+    LIteratorEnd *lir = new LIteratorEnd(useRegister(ins->iterator()), temp(), temp());
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitStringLength(MStringLength *ins)
 {
     JS_ASSERT(ins->string()->type() == MIRType_String);
     return define(new LStringLength(useRegisterAtStart(ins->string())), ins);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -304,16 +304,18 @@ js::NewContext(JSRuntime *rt, size_t sta
 }
 
 void
 js::DestroyContext(JSContext *cx, DestroyContextMode mode)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
+    JS_ASSERT(!cx->enumerators);
+
 #ifdef JS_THREADSAFE
     JS_ASSERT(cx->outstandingRequests == 0);
 #endif
 
     if (mode != DCM_NEW_FAILED) {
         if (JSContextCallback cxCallback = rt->cxCallback) {
             /*
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
@@ -1118,16 +1120,17 @@ JSContext::JSContext(JSRuntime *rt)
     rngSeed(0),
     iterValue(MagicValue(JS_NO_ITER_VALUE)),
 #ifdef JS_METHODJIT
     methodJitEnabled(false),
 #endif
 #ifdef MOZ_TRACE_JSCALLS
     functionCallback(NULL),
 #endif
+    enumerators(NULL),
     innermostGenerator_(NULL),
 #ifdef DEBUG
     stackIterAssertionEnabled(true),
 #endif
     activeCompilations(0)
 {
     JS_ASSERT(static_cast<ContextFriendFields*>(this) ==
               ContextFriendFields::get(this));
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -140,20 +140,16 @@ JSCompartment::init(JSContext *cx)
         if (!gcStoreBuffer.enable())
             return false;
     } else {
         gcNursery.disable();
         gcStoreBuffer.disable();
     }
 #endif
 
-    enumerators = NativeIterator::allocateSentinel(cx);
-    if (!enumerators)
-        return false;
-
     return debuggees.init();
 }
 
 void
 JSCompartment::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
 {
 #ifdef JS_METHODJIT
     /* ClearAllFrames calls compileBarriers() and needs the old value. */
@@ -728,25 +724,16 @@ JSCompartment::sweep(FreeOp *fop, bool r
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
             rt->freeLifoAlloc.transferFrom(&analysisLifoAlloc);
             rt->freeLifoAlloc.transferFrom(&oldAlloc);
         }
     }
 
-    NativeIterator *ni = enumerators->next();
-    while (ni != enumerators) {
-        JSObject *iterObj = ni->iterObj();
-        NativeIterator *next = ni->next();
-        if (JS_IsAboutToBeFinalized(iterObj))
-            ni->unlink();
-        ni = next;
-    }
-
     active = false;
 }
 
 /*
  * Remove dead wrappers from the table. We must sweep all compartments, since
  * string entries in the crossCompartmentWrappers table are not marked during
  * markCrossCompartmentWrappers.
  */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -24,18 +24,16 @@
 #include "vm/RegExpObject.h"
 
 namespace js {
 
 namespace ion {
     class IonCompartment;
 }
 
-struct NativeIterator;
-
 /*
  * A single-entry cache for some base-10 double-to-string conversions. This
  * helps date-format-xparb.js.  It also avoids skewing the results for
  * v8-splay.js when measured by the SunSpider harness, where the splay tree
  * initialization (which includes many repeated double-to-string conversions)
  * is erroneously included in the measurement; see bug 562553.
  */
 class DtoaCache {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -298,21 +298,23 @@ js::RunScript(JSContext *cx, HandleScrip
 #endif
 
     JS_CHECK_RECURSION(cx, return false);
 
 #ifdef DEBUG
     struct CheckStackBalance {
         JSContext *cx;
         StackFrame *fp;
+        RootedObject enumerators;
         CheckStackBalance(JSContext *cx)
-          : cx(cx), fp(cx->fp())
+          : cx(cx), fp(cx->fp()), enumerators(cx, cx->enumerators)
         {}
         ~CheckStackBalance() {
             JS_ASSERT(fp == cx->fp());
+            JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators);
         }
     } check(cx);
 #endif
 
     SPSEntryMarker marker(cx->runtime);
 
 #ifdef JS_ION
     if (ion::IsEnabled(cx)) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -441,52 +441,36 @@ NativeIterator::allocateIterator(JSConte
     if (plength) {
         for (size_t i = 0; i < plength; i++) {
             JSFlatString *str = IdToString(cx, props[i]);
             if (!str || !strings.append(StringValue(str)))
                 return NULL;
             ni->props_array[i].init(str);
         }
     }
-    ni->next_ = NULL;
-    ni->prev_ = NULL;
-    return ni;
-}
-
-NativeIterator *
-NativeIterator::allocateSentinel(JSContext *cx)
-{
-    NativeIterator *ni = (NativeIterator *)js_malloc(sizeof(NativeIterator));
-    if (!ni)
-        return NULL;
-
-    PodZero(ni);
-
-    ni->next_ = ni;
-    ni->prev_ = ni;
     return ni;
 }
 
 inline void
-NativeIterator::init(RawObject obj, RawObject iterObj, unsigned flags, uint32_t slength, uint32_t key)
+NativeIterator::init(RawObject obj, unsigned flags, uint32_t slength, uint32_t key)
 {
     this->obj.init(obj);
-    this->iterObj_ = iterObj;
     this->flags = flags;
     this->shapes_array = (Shape **) this->props_end;
     this->shapes_length = slength;
     this->shapes_key = key;
 }
 
 static inline void
 RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
 {
     /* Register non-escaping native enumerators (for-in) with the current context. */
     if (ni->flags & JSITER_ENUMERATE) {
-        ni->link(cx->compartment->enumerators);
+        ni->next = cx->enumerators;
+        cx->enumerators = iterobj;
 
         JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
         ni->flags |= JSITER_ACTIVE;
     }
 }
 
 static inline bool
 VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
@@ -502,17 +486,17 @@ VectorToKeyIterator(JSContext *cx, Handl
 
     Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
     if (!ni)
         return false;
-    ni->init(obj, iterobj, flags, slength, key);
+    ni->init(obj, flags, slength, key);
 
     if (slength) {
         /*
          * Fill in the shape array from scratch.  We can't use the array that was
          * computed for the cache lookup earlier, as constructing iterobj could
          * have triggered a shape-regenerating GC.  Don't bother with regenerating
          * the shape key; if such a GC *does* occur, we can only get hits through
          * the one-slot lastNativeIterator cache.
@@ -554,17 +538,17 @@ js::VectorToValueIterator(JSContext *cx,
 
     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
     if (!ni)
         return false;
-    ni->init(obj, iterobj, flags, 0, 0);
+    ni->init(obj, flags, 0, 0);
 
     iterobj->setNativeIterator(ni);
     vp.setObject(*iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
     return true;
 }
 
@@ -1031,17 +1015,18 @@ js::CloseIterator(JSContext *cx, HandleO
 {
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
 
     if (obj->isPropertyIterator()) {
         /* Remove enumerators from the active list, which is a stack. */
         NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
 
         if (ni->flags & JSITER_ENUMERATE) {
-            ni->unlink();
+            JS_ASSERT(cx->enumerators == obj);
+            cx->enumerators = ni->next;
 
             JS_ASSERT(ni->flags & JSITER_ACTIVE);
             ni->flags &= ~JSITER_ACTIVE;
 
             /*
              * Reset the enumerator; it may still be in the cached iterators
              * for this thread, and can be reused.
              */
@@ -1067,18 +1052,20 @@ js::UnwindIteratorForException(JSContext
     return true;
 }
 
 void
 js::UnwindIteratorForUncatchableException(JSContext *cx, RawObject obj)
 {
     if (obj->isPropertyIterator()) {
         NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
-        if (ni->flags & JSITER_ENUMERATE)
-            ni->unlink();
+        if (ni->flags & JSITER_ENUMERATE) {
+            JS_ASSERT(cx->enumerators == obj);
+            cx->enumerators = ni->next;
+        }
     }
 }
 
 /*
  * Suppress enumeration of deleted properties. This function must be called
  * when a property is deleted and there might be active enumerators.
  *
  * We maintain a list of active non-escaping for-in enumerators. To suppress
@@ -1093,34 +1080,27 @@ js::UnwindIteratorForUncatchableExceptio
  * argument is an object which can be called on an id and returns true or
  * false. It also must have a method |matchesAtMostOne| which allows us to
  * stop searching after the first deletion if true.
  */
 template<typename StringPredicate>
 static bool
 SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
 {
-    NativeIterator *enumeratorList = cx->compartment->enumerators;
-    NativeIterator *ni = enumeratorList->next();
-
-    while (ni != enumeratorList) {
+    PropertyIteratorObject *iterobj = cx->enumerators;
+    while (iterobj) {
       again:
+        NativeIterator *ni = iterobj->getNativeIterator();
         /* This only works for identified surpressed keys, not values. */
         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
             /* Check whether id is still to come. */
             HeapPtr<JSFlatString> *props_cursor = ni->current();
             HeapPtr<JSFlatString> *props_end = ni->end();
             for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
                 if (predicate(*idp)) {
-                     /*
-                     * Root the iterobj. This loop can GC, so we want to make sure that if
-                     * the GC removes any elements from the list, it won't remove this one.
-                     */
-                    AutoObjectRooter iterRoot(cx, ni->iterObj());
-
                     /*
                      * Check whether another property along the prototype chain
                      * became visible as a result of this deletion.
                      */
                     RootedObject proto(cx);
                     if (!JSObject::getProto(cx, obj, &proto))
                         return false;
                     if (proto) {
@@ -1173,17 +1153,17 @@ SuppressDeletedPropertyHelper(JSContext 
                     /* Don't reuse modified native iterators. */
                     ni->flags |= JSITER_UNREUSABLE;
 
                     if (predicate.matchesAtMostOne())
                         break;
                 }
             }
         }
-        ni = ni->next();
+        iterobj = ni->next;
     }
     return true;
 }
 
 class SingleStringPredicate {
     Handle<JSFlatString*> str;
 public:
     SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
@@ -1491,16 +1471,17 @@ js_NewGenerator(JSContext *cx)
 
     /* Cut up floatingStack space. */
     HeapValue *genvp = gen->stackSnapshot;
     StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen);
 
     /* Initialize JSGenerator. */
     gen->obj.init(obj);
     gen->state = JSGEN_NEWBORN;
+    gen->enumerators = NULL;
     gen->fp = genfp;
     gen->prevGenerator = NULL;
 
     /* Copy from the stack to the generator's floating frame. */
     gen->regs.rebaseFromTo(stackRegs, *genfp);
     genfp->copyFrameAndValues<StackFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
                                                          stackvp, stackRegs.sp);
 
@@ -1586,20 +1567,24 @@ SendToGenerator(JSContext *cx, JSGenerat
          * or else we might fail to scan some generator values.
          */
         gen->state = futureState;
 
         StackFrame *fp = gfg.fp();
         gen->regs = cx->regs();
 
         cx->enterGenerator(gen);   /* OOM check above. */
+        PropertyIteratorObject *enumerators = cx->enumerators;
+        cx->enumerators = gen->enumerators;
 
         RootedScript script(cx, fp->script());
         ok = RunScript(cx, script, fp);
 
+        gen->enumerators = cx->enumerators;
+        cx->enumerators = enumerators;
         cx->leaveGenerator(gen);
     }
 
     if (gen->fp->isYielding()) {
         /* Yield cannot fail, throw or be called on closing. */
         JS_ASSERT(ok);
         JS_ASSERT(!cx->isExceptionPending());
         JS_ASSERT(gen->state == JSGEN_RUNNING);
@@ -1801,17 +1786,17 @@ GlobalObject::initIteratorClasses(JSCont
         iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
         if (!iteratorProto)
             return false;
 
         AutoIdVector blank(cx);
         NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
         if (!ni)
             return false;
-        ni->init(NULL, NULL, 0 /* flags */, 0, 0);
+        ni->init(NULL, 0 /* flags */, 0, 0);
 
         iteratorProto->asPropertyIterator().setNativeIterator(ni);
 
         Rooted<JSFunction*> ctor(cx);
         ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
         if (!ctor)
             return false;
         if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -25,99 +25,54 @@
  */
 #define JSITER_ACTIVE       0x1000
 #define JSITER_UNREUSABLE   0x2000
 
 namespace js {
 
 struct NativeIterator
 {
-    HeapPtrObject obj;                  // Object being iterated.
-    JSObject *iterObj_;                 // Internal iterator object.
+    HeapPtrObject obj;
     HeapPtr<JSFlatString> *props_array;
     HeapPtr<JSFlatString> *props_cursor;
     HeapPtr<JSFlatString> *props_end;
     Shape **shapes_array;
     uint32_t shapes_length;
     uint32_t shapes_key;
     uint32_t flags;
+    PropertyIteratorObject *next;  /* Forms cx->enumerators list, garbage otherwise. */
 
-  private:
-    /* While in compartment->enumerators, these form a doubly linked list. */
-    NativeIterator *next_;
-    NativeIterator *prev_;
-
-  public:
-    bool isKeyIter() const {
-        return (flags & JSITER_FOREACH) == 0;
-    }
+    bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
 
     inline HeapPtr<JSFlatString> *begin() const {
         return props_array;
     }
 
     inline HeapPtr<JSFlatString> *end() const {
         return props_end;
     }
 
     size_t numKeys() const {
         return end() - begin();
     }
 
-    JSObject *iterObj() const {
-        return iterObj_;
-    }
     HeapPtr<JSFlatString> *current() const {
         JS_ASSERT(props_cursor < props_end);
         return props_cursor;
     }
 
-    NativeIterator *next() {
-        return next_;
-    }
-
-    static inline size_t offsetOfNext() {
-        return offsetof(NativeIterator, next_);
-    }
-    static inline size_t offsetOfPrev() {
-        return offsetof(NativeIterator, prev_);
-    }
-
     void incCursor() {
         props_cursor = props_cursor + 1;
     }
-    void link(NativeIterator *other) {
-        /* A NativeIterator cannot appear in the enumerator list twice. */
-        JS_ASSERT(!next_ && !prev_);
-        JS_ASSERT(flags & JSITER_ENUMERATE);
 
-        this->next_ = other;
-        this->prev_ = other->prev_;
-        other->prev_->next_ = this;
-        other->prev_ = this;
-    }
-    void unlink() {
-        JS_ASSERT(flags & JSITER_ENUMERATE);
-
-        next_->prev_ = prev_;
-        prev_->next_ = next_;
-        next_ = NULL;
-        prev_ = NULL;
-    }
-
-    static NativeIterator *allocateSentinel(JSContext *cx);
     static NativeIterator *allocateIterator(JSContext *cx, uint32_t slength,
                                             const js::AutoIdVector &props);
-    void init(RawObject obj, RawObject iterObj, unsigned flags, uint32_t slength, uint32_t key);
+    void init(RawObject obj, unsigned flags, uint32_t slength, uint32_t key);
 
     void mark(JSTracer *trc);
-
-    static void destroy(NativeIterator *iter) {
-        js_free(iter);
-    }
 };
 
 class PropertyIteratorObject : public JSObject
 {
   public:
     static Class class_;
 
     inline NativeIterator *getNativeIterator() const;
@@ -337,16 +292,17 @@ enum JSGeneratorState
     JSGEN_CLOSED    /* closed, cannot be started or closed again */
 };
 
 struct JSGenerator
 {
     js::HeapPtrObject   obj;
     JSGeneratorState    state;
     js::FrameRegs       regs;
+    js::PropertyIteratorObject *enumerators;
     JSGenerator         *prevGenerator;
     js::StackFrame      *fp;
     js::HeapValue       stackSnapshot[1];
 };
 
 extern JSObject *
 js_NewGenerator(JSContext *cx);
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6232,31 +6232,20 @@ mjit::Compiler::iter(unsigned flags)
 
     /* Mark iterator as active. */
     masm.storePtr(reg, Address(nireg, offsetof(NativeIterator, obj)));
     masm.load32(flagsAddr, T1);
     masm.or32(Imm32(JSITER_ACTIVE), T1);
     masm.store32(T1, flagsAddr);
 
     /* Chain onto the active iterator stack. */
-    masm.move(ImmPtr(cx->compartment), T1);
-    masm.loadPtr(Address(T1, offsetof(JSCompartment, enumerators)), T1);
-
-    /* ni->next = list */
-    masm.storePtr(T1, Address(nireg, NativeIterator::offsetOfNext()));
-
-    /* ni->prev = list->prev */
-    masm.loadPtr(Address(T1, NativeIterator::offsetOfPrev()), T2);
-    masm.storePtr(T2, Address(nireg, NativeIterator::offsetOfPrev()));
-
-    /* list->prev->next = ni */
-    masm.storePtr(nireg, Address(T2, NativeIterator::offsetOfNext()));
-
-    /* list->prev = ni */
-    masm.storePtr(nireg, Address(T1, NativeIterator::offsetOfPrev()));
+    masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), T1);
+    masm.loadPtr(Address(T1, offsetof(JSContext, enumerators)), T2);
+    masm.storePtr(T2, Address(nireg, offsetof(NativeIterator, next)));
+    masm.storePtr(ioreg, Address(T1, offsetof(JSContext, enumerators)));
 
     frame.freeReg(nireg);
     frame.freeReg(T1);
     frame.freeReg(T2);
 
     stubcc.leave();
     stubcc.masm.move(Imm32(flags), Registers::ArgReg1);
     OOL_STUBCALL(stubs::Iter, REJOIN_FALLTHROUGH);
@@ -6397,32 +6386,23 @@ mjit::Compiler::iterEnd()
     /* Clear active bit. */
     masm.and32(Imm32(~JSITER_ACTIVE), T2);
     masm.storePtr(T2, flagAddr);
 
     /* Reset property cursor. */
     masm.loadPtr(Address(T1, offsetof(NativeIterator, props_array)), T2);
     masm.storePtr(T2, Address(T1, offsetof(NativeIterator, props_cursor)));
 
-    /* Unlink from the iterator list. */
-    RegisterID prev = T2;
-    RegisterID next = frame.allocReg();
-
-    masm.loadPtr(Address(T1, NativeIterator::offsetOfNext()), next);
-    masm.loadPtr(Address(T1, NativeIterator::offsetOfPrev()), prev);
-    masm.storePtr(prev, Address(next, NativeIterator::offsetOfPrev()));
-    masm.storePtr(next, Address(prev, NativeIterator::offsetOfNext()));
-#ifdef DEBUG
-    masm.storePtr(ImmPtr(NULL), Address(T1, NativeIterator::offsetOfNext()));
-    masm.storePtr(ImmPtr(NULL), Address(T1, NativeIterator::offsetOfPrev()));
-#endif
+    /* Advance enumerators list. */
+    masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), T2);
+    masm.loadPtr(Address(T1, offsetof(NativeIterator, next)), T1);
+    masm.storePtr(T1, Address(T2, offsetof(JSContext, enumerators)));
 
     frame.freeReg(T1);
     frame.freeReg(T2);
-    frame.freeReg(next);
 
     stubcc.leave();
     OOL_STUBCALL(stubs::EndIter, REJOIN_FALLTHROUGH);
 
     frame.pop();
 
     stubcc.rejoin(Changes(1));
 }