Backed out changeset a29d7f5ddde6 (bug 932982) for ggc asserts.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Dec 2013 13:56:32 -0500
changeset 176258 afa4bcb0150e7a32900361b2e2c910220f9057cf
parent 176257 551bf09ad92e3844031143b7bfca219fc9635e63
child 176259 ad9dc3482bd429cb7077eaac7bd53e43ba49b957
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs932982
milestone29.0a1
backs outa29d7f5ddde63e437958b32305bd9896c210f750
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 a29d7f5ddde6 (bug 932982) for ggc asserts.
js/public/MemoryMetrics.h
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCode.h
js/src/jit/MIR.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/vm/MemoryMetrics.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -388,16 +388,17 @@ struct CompartmentStats
     macro(Other,   NotLiveGCThing, shapesMallocHeapCompartmentTables) \
     macro(Other,   IsLiveGCThing,  scriptsGCHeap) \
     macro(Other,   NotLiveGCThing, scriptsMallocHeapData) \
     macro(Other,   NotLiveGCThing, baselineData) \
     macro(Other,   NotLiveGCThing, baselineStubsFallback) \
     macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
     macro(Other,   NotLiveGCThing, ionData) \
     macro(Other,   NotLiveGCThing, typeInferenceTypeScripts) \
+    macro(Other,   NotLiveGCThing, typeInferencePendingArrays) \
     macro(Other,   NotLiveGCThing, typeInferenceAllocationSiteTables) \
     macro(Other,   NotLiveGCThing, typeInferenceArrayTypeTables) \
     macro(Other,   NotLiveGCThing, typeInferenceObjectTypeTables) \
     macro(Other,   NotLiveGCThing, compartmentObject) \
     macro(Other,   NotLiveGCThing, crossCompartmentWrappersTable) \
     macro(Other,   NotLiveGCThing, regexpCompartment) \
     macro(Other,   NotLiveGCThing, debuggeesSet)
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1117,21 +1117,24 @@ gc::MarkCycleCollectorChildren(JSTracer 
         MarkId(trc, &shape->propidRef(), "propid");
         shape = shape->previous();
     } while (shape);
 }
 
 static void
 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
 {
-    unsigned count = type->getPropertyCount();
-    for (unsigned i = 0; i < count; i++) {
-        types::Property *prop = type->getProperty(i);
-        if (prop && JSID_IS_STRING(prop->id))
-            PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
+    /* Don't mark properties for singletons. They'll be purged by the GC. */
+    if (!type->singleton) {
+        unsigned count = type->getPropertyCount();
+        for (unsigned i = 0; i < count; i++) {
+            types::Property *prop = type->getProperty(i);
+            if (prop && JSID_IS_STRING(prop->id))
+                PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
+        }
     }
 
     if (TaggedProto(type->proto).isObject())
         PushMarkStack(gcmarker, type->proto);
 
     if (type->singleton && !type->lazy())
         PushMarkStack(gcmarker, type->singleton);
 
@@ -1342,30 +1345,59 @@ GCMarker::restoreValueArray(JSObject *ob
         }
     }
 
     JS_ASSERT(*vpp <= *endp);
     return true;
 }
 
 void
-GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
+GCMarker::processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr)
 {
     if (tag == TypeTag) {
         ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
     } else if (tag == SavedValueArrayTag) {
         JS_ASSERT(!(addr & CellMask));
         JSObject *obj = reinterpret_cast<JSObject *>(addr);
         HeapValue *vp, *end;
         if (restoreValueArray(obj, (void **)&vp, (void **)&end))
             pushValueArray(obj, vp, end);
         else
             pushObject(obj);
     } else if (tag == IonCodeTag) {
         MarkChildren(this, reinterpret_cast<jit::IonCode *>(addr));
+    } else if (tag == ArenaTag) {
+        ArenaHeader *aheader = reinterpret_cast<ArenaHeader *>(addr);
+        AllocKind thingKind = aheader->getAllocKind();
+        size_t thingSize = Arena::thingSize(thingKind);
+
+        for ( ; aheader; aheader = aheader->next) {
+            Arena *arena = aheader->getArena();
+            FreeSpan firstSpan(aheader->getFirstFreeSpan());
+            const FreeSpan *span = &firstSpan;
+
+            for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) {
+                JS_ASSERT(thing <= arena->thingsEnd());
+                if (thing == span->first) {
+                    if (!span->hasNext())
+                        break;
+                    thing = span->last;
+                    span = span->nextSpan();
+                } else {
+                    JSObject *object = reinterpret_cast<JSObject *>(thing);
+                    if (object->hasSingletonType() && object->markIfUnmarked(getMarkColor()))
+                        pushObject(object);
+                    budget.step();
+                }
+            }
+            if (budget.isOverBudget()) {
+                pushArenaList(aheader);
+                return;
+            }
+        }
     }
 }
 
 inline void
 GCMarker::processMarkStackTop(SliceBudget &budget)
 {
     /*
      * The function uses explicit goto and implements the scanning of the
@@ -1393,17 +1425,17 @@ GCMarker::processMarkStackTop(SliceBudge
     }
 
     if (tag == ObjectTag) {
         obj = reinterpret_cast<JSObject *>(addr);
         JS_COMPARTMENT_ASSERT(runtime, obj);
         goto scan_obj;
     }
 
-    processMarkStackOther(tag, addr);
+    processMarkStackOther(budget, tag, addr);
     return;
 
   scan_value_array:
     JS_ASSERT(vp <= end);
     while (vp != end) {
         const Value &v = *vp++;
         if (v.isString()) {
             JSString *str = v.toString();
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -721,16 +721,21 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
 
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting())
             continue;
 
+        if (IS_GC_MARKING_TRACER(trc) && zone->isPreservingCode()) {
+            gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES);
+            zone->markTypes(trc);
+        }
+
         /* Do not discard scripts with counts while profiling. */
         if (rt->profilingScripts && !rt->isHeapMinorCollecting()) {
             for (CellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->hasScriptCounts()) {
                     MarkScriptRoot(trc, &script, "profilingScripts");
                     JS_ASSERT(script == i.get<JSScript>());
                 }
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -271,16 +271,17 @@ static const Phase PHASE_NO_PARENT = PHA
 
 static const PhaseInfo phases[] = {
     { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT },
     { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT },
     { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT },
     { PHASE_PURGE, "Purge", PHASE_NO_PARENT },
     { PHASE_MARK, "Mark", PHASE_NO_PARENT },
     { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK },
+    { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS },
     { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
     { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
     { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
     { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK },
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -23,16 +23,17 @@ namespace gcstats {
 
 enum Phase {
     PHASE_GC_BEGIN,
     PHASE_WAIT_BACKGROUND_THREAD,
     PHASE_MARK_DISCARD_CODE,
     PHASE_PURGE,
     PHASE_MARK,
     PHASE_MARK_ROOTS,
+    PHASE_MARK_TYPES,
     PHASE_MARK_DELAYED,
     PHASE_SWEEP,
     PHASE_SWEEP_MARK,
     PHASE_SWEEP_MARK_TYPES,
     PHASE_SWEEP_MARK_INCOMING_BLACK,
     PHASE_SWEEP_MARK_WEAK,
     PHASE_SWEEP_MARK_INCOMING_GRAY,
     PHASE_SWEEP_MARK_GRAY,
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -76,16 +76,45 @@ Zone::setNeedsBarrier(bool needs, Should
     if (needs && runtimeFromMainThread()->isAtomsZone(this))
         JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
 
     JS_ASSERT_IF(needs, canCollect());
     needsBarrier_ = needs;
 }
 
 void
+Zone::markTypes(JSTracer *trc)
+{
+    /*
+     * Mark all scripts, type objects and singleton JS objects in the
+     * compartment. These can be referred to directly by type sets, which we
+     * cannot modify while code which depends on these type sets is active.
+     */
+    JS_ASSERT(isPreservingCode());
+
+    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
+        MarkScriptRoot(trc, &script, "mark_types_script");
+        JS_ASSERT(script == i.get<JSScript>());
+    }
+
+    for (size_t thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
+        ArenaHeader *aheader = allocator.arenas.getFirstArena(static_cast<AllocKind>(thingKind));
+        if (aheader)
+            trc->runtime->gcMarker.pushArenaList(aheader);
+    }
+
+    for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
+        types::TypeObject *type = i.get<types::TypeObject>();
+        MarkTypeObjectRoot(trc, &type, "mark_types_scan");
+        JS_ASSERT(type == i.get<types::TypeObject>());
+    }
+}
+
+void
 Zone::resetGCMallocBytes()
 {
     gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
     gcMallocGCTriggered = false;
 }
 
 void
 Zone::setGCMaxMallocBytes(size_t value)
@@ -110,17 +139,17 @@ Zone::sweep(FreeOp *fop, bool releaseTyp
 {
     /*
      * Periodically release observed types for all scripts. This is safe to
      * do when there are no frames for the zone on the stack.
      */
     if (active)
         releaseTypes = false;
 
-    {
+    if (!isPreservingCode()) {
         gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
         types.sweep(fop, releaseTypes);
     }
 
     if (!fop->runtime()->debuggerList.isEmpty())
         sweepBreakpoints(fop);
 
     active = false;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -305,16 +305,18 @@ struct Zone : public JS::shadow::Zone,
 
     void *onOutOfMemory(void *p, size_t nbytes) {
         return runtimeFromMainThread()->onOutOfMemory(p, nbytes);
     }
     void reportAllocationOverflow() {
         js_ReportAllocationOverflow(nullptr);
     }
 
+    void markTypes(JSTracer *trc);
+
     js::types::TypeZone types;
 
     void sweep(js::FreeOp *fop, bool releaseTypes);
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
 };
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5885,17 +5885,17 @@ CodeGenerator::link(JSContext *cx, types
       IonScript::New(cx, recompileInfo,
                      graph.totalSlotCount(), scriptFrameSize, snapshots_.size(),
                      bailouts_.length(), graph.numConstants(),
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
                      patchableBackedges_.length(), optimizationLevel);
     if (!ionScript) {
-        recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
+        recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
         return false;
     }
 
     // Lock the runtime against operation callbacks during the link.
     // We don't want an operation callback to protect the code for the script
     // before it has been filled in, as we could segv before the runtime's
     // patchable backedges have been fully updated.
     JSRuntime::AutoLockForOperationCallback lock(cx->runtime());
@@ -5910,17 +5910,17 @@ CodeGenerator::link(JSContext *cx, types
     Linker linker(masm);
     IonCode *code = (executionMode == SequentialExecution)
                     ? linker.newCodeForIonScript(cx)
                     : linker.newCode<CanGC>(cx, JSC::ION_CODE);
     if (!code) {
         // Use js_free instead of IonScript::Destroy: the cache list and
         // backedge list are still uninitialized.
         js_free(ionScript);
-        recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
+        recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
         return false;
     }
 
     ionScript->setMethod(code);
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
@@ -5931,17 +5931,17 @@ CodeGenerator::link(JSContext *cx, types
     if (HasIonScript(script, executionMode)) {
         JS_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
         // Do a normal invalidate, except don't cancel offThread compilations,
         // since that will cancel this compilation too.
         if (!Invalidate(cx, script, SequentialExecution,
                         /* resetUses */ false, /* cancelOffThread*/ false))
         {
             js_free(ionScript);
-            recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
+            recompileInfo.compilerOutput(cx->compartment()->types)->invalidate();
             return false;
         }
     }
 
     SetIonScript(script, executionMode, ionScript);
 
     // In parallel execution mode, when we first compile a script, we
     // don't know that its potential callees are compiled, so set a
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2503,17 +2503,17 @@ jit::InvalidateAll(FreeOp *fop, Zone *zo
             IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
             InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 }
 
 
 void
-jit::Invalidate(types::TypeZone &types, FreeOp *fop,
+jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses,
                 bool cancelOffThread)
 {
     IonSpew(IonSpew_Invalidate, "Start invalidation.");
     AutoFlushCache afc ("Invalidate", fop->runtime()->jitRuntime());
 
     // Add an invalidation reference to all invalidated IonScripts to indicate
     // to the traversal which frames have been invalidated.
@@ -2586,17 +2586,17 @@ jit::Invalidate(types::TypeZone &types, 
     // multiple times in the above loop.
     JS_ASSERT(!numInvalidations);
 }
 
 void
 jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
                 bool cancelOffThread)
 {
-    jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
+    jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
                     cancelOffThread);
 }
 
 bool
 jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
                 bool cancelOffThread)
 {
     JS_ASSERT(script->hasIonScript());
@@ -2633,23 +2633,24 @@ FinishInvalidationOf(FreeOp *fop, JSScri
 {
     // In all cases, nullptr out script->ion or script->parallelIon to avoid
     // re-entry.
     if (parallel)
         script->setParallelIonScript(nullptr);
     else
         script->setIonScript(nullptr);
 
-    types::TypeZone &types = script->zone()->types;
-    ionScript->recompileInfo().compilerOutput(types)->invalidate();
-
-    // If this script has Ion code on the stack, invalidated() will return
+    // If this script has Ion code on the stack, invalidation() will return
     // true. In this case we have to wait until destroying it.
-    if (!ionScript->invalidated())
+    if (!ionScript->invalidated()) {
+        types::TypeCompartment &types = script->compartment()->types;
+        ionScript->recompileInfo().compilerOutput(types)->invalidate();
+
         jit::IonScript::Destroy(fop, ionScript);
+    }
 }
 
 void
 jit::FinishInvalidation(FreeOp *fop, JSScript *script)
 {
     if (script->hasIonScript())
         FinishInvalidationOf(fop, script, script->ionScript(), false);
 
@@ -2658,16 +2659,18 @@ jit::FinishInvalidation(FreeOp *fop, JSS
 }
 
 void
 jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp)
 {
     // Free optimized baseline stubs.
     if (comp->jitCompartment())
         comp->jitCompartment()->optimizedStubSpace()->free();
+
+    comp->types.clearCompilerOutputs(fop);
 }
 
 void
 jit::MarkValueFromIon(JSRuntime *rt, Value *vp)
 {
     gc::MarkValueUnbarriered(&rt->gcMarker, vp, "write barrier");
 }
 
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -122,17 +122,17 @@ struct EnterJitData;
 bool SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals);
 
 IonExecStatus IonCannon(JSContext *cx, RunState &state);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
-void Invalidate(types::TypeZone &types, FreeOp *fop,
+void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
                 bool cancelOffThread = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
                 bool cancelOffThread = true);
 bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true,
                 bool cancelOffThread = true);
 bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true,
                 bool cancelOffThread = true);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6316,17 +6316,17 @@ IonBuilder::getStaticName(JSObject *stat
     if (staticType->unknownProperties()) {
         *psucceeded = false;
         return true;
     }
 
     types::HeapTypeSetKey property = staticType->property(id);
     if (!property.maybeTypes() ||
         !property.maybeTypes()->definiteProperty() ||
-        property.configured(constraints()))
+        property.configured(constraints(), staticType))
     {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         *psucceeded = false;
         return true;
     }
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
@@ -6406,17 +6406,17 @@ IonBuilder::setStaticName(JSObject *stat
 
     types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
     if (staticType->unknownProperties())
         return jsop_setprop(name);
 
     types::HeapTypeSetKey property = staticType->property(id);
     if (!property.maybeTypes() ||
         !property.maybeTypes()->definiteProperty() ||
-        property.configured(constraints()))
+        property.configured(constraints(), staticType))
     {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
 
     if (!TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet()))
         return jsop_setprop(name);
@@ -7856,17 +7856,17 @@ IonBuilder::getDefiniteSlot(types::Tempo
     if (type->unknownProperties() || type->singleton())
         return false;
 
     jsid id = NameToId(name);
 
     *property = type->property(id);
     return property->maybeTypes() &&
            property->maybeTypes()->definiteProperty() &&
-           !property->configured(constraints());
+           !property->configured(constraints(), type);
 }
 
 bool
 IonBuilder::jsop_runonce()
 {
     MRunOncePrologue *ins = MRunOncePrologue::New(alloc());
     current->add(ins);
     return resumeAfter(ins);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -526,19 +526,16 @@ struct IonScript
         JS_ASSERT(refcount_);
         refcount_--;
         if (!refcount_)
             Destroy(fop, this);
     }
     const types::RecompileInfo& recompileInfo() const {
         return recompileInfo_;
     }
-    types::RecompileInfo& recompileInfoRef() {
-        return recompileInfo_;
-    }
     OptimizationLevel optimizationLevel() const {
         return optimizationLevel_;
     }
     uint32_t incrOsrPcMismatchCounter() {
         return ++osrPcMismatchCounter_;
     }
     void resetOsrPcMismatchCounter() {
         osrPcMismatchCounter_ = 0;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3020,17 +3020,17 @@ jit::PropertyReadIsIdempotent(types::Com
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObjectKey *object = types->getObject(i);
         if (object) {
             if (object->unknownProperties())
                 return false;
 
             // Check if the property has been reconfigured or is a getter.
             types::HeapTypeSetKey property = object->property(NameToId(name));
-            if (property.configured(constraints))
+            if (property.configured(constraints, object))
                 return false;
         }
     }
 
     return true;
 }
 
 bool
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -43,16 +43,17 @@ JSCompartment::JSCompartment(Zone *zone,
     principals(nullptr),
     isSystem(false),
     marked(true),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
+    lastCodeRelease(0),
     data(nullptr),
     objectMetadataCallback(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
     typeReprs(runtime_),
     globalWriteBarriered(false),
     propertyTree(thisForCtor()),
     gcIncomingGrayPointers(nullptr),
@@ -539,16 +540,23 @@ JSCompartment::sweep(FreeOp *fop, bool r
 
         if (debugScopes)
             debugScopes->sweep(rt);
 
         /* Finalize unreachable (key,value) pairs in all weak maps. */
         WeakMapBase::sweepCompartment(this);
     }
 
+    if (zone()->isPreservingCode()) {
+        gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
+        types.sweepShapes(fop);
+    } else {
+        JS_ASSERT(!types.constrainedOutputs);
+    }
+
     NativeIterator *ni = enumerators->next();
     while (ni != enumerators) {
         JSObject *iterObj = ni->iterObj();
         NativeIterator *next = ni->next();
         if (gc::IsObjectAboutToBeFinalized(&iterObj))
             ni->unlink();
         ni = next;
     }
@@ -851,28 +859,29 @@ JSCompartment::clearTraps(FreeOp *fop)
         JSScript *script = i.get<JSScript>();
         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearTraps(fop);
     }
 }
 
 void
 JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                      size_t *tiPendingArrays,
                                       size_t *tiAllocationSiteTables,
                                       size_t *tiArrayTypeTables,
                                       size_t *tiObjectTypeTables,
                                       size_t *compartmentObject,
                                       size_t *shapesCompartmentTables,
                                       size_t *crossCompartmentWrappersArg,
                                       size_t *regexpCompartment,
                                       size_t *debuggeesSet,
                                       size_t *baselineStubsOptimized)
 {
     *compartmentObject += mallocSizeOf(this);
-    types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
+    types.addSizeOfExcludingThis(mallocSizeOf, tiPendingArrays, tiAllocationSiteTables,
                                  tiArrayTypeTables, tiObjectTypeTables);
     *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
                               + initialShapes.sizeOfExcludingThis(mallocSizeOf)
                               + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
                               + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
     *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
     *debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -182,16 +182,18 @@ struct JSCompartment
   public:
     /*
      * Moves all data from the allocator |workerAllocator|, which was
      * in use by a parallel worker, into the compartment's main
      * allocator.  This is used at the end of a parallel section.
      */
     void adoptWorkerAllocator(js::Allocator *workerAllocator);
 
+
+    int64_t                      lastCodeRelease;
     bool                         activeAnalysis;
 
     /* Type information about the scripts and objects in this compartment. */
     js::types::TypeCompartment   types;
 
     void                         *data;
 
   private:
@@ -214,16 +216,17 @@ struct JSCompartment
      *
      * This is used to avoid adding it to the store buffer on every write, which
      * can quickly fill the buffer and also cause performance problems.
      */
     bool                         globalWriteBarriered;
 
   public:
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                size_t *tiPendingArrays,
                                 size_t *tiAllocationSiteTables,
                                 size_t *tiArrayTypeTables,
                                 size_t *tiObjectTypeTables,
                                 size_t *compartmentObject,
                                 size_t *shapesCompartmentTables,
                                 size_t *crossCompartmentWrappers,
                                 size_t *regexpCompartment,
                                 size_t *debuggeesSet,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2849,19 +2849,23 @@ static bool
 ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
 {
     JSRuntime *rt = comp->runtimeFromMainThread();
     if (rt->gcShouldCleanUpEverything || !comp->zone()->types.inferenceEnabled)
         return false;
 
     if (rt->alwaysPreserveCode)
         return true;
-    if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
+    if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime &&
+        comp->lastCodeRelease + (PRMJ_USEC_PER_SEC * 300) >= currentTime)
+    {
         return true;
-
+    }
+
+    comp->lastCodeRelease = currentTime;
     return false;
 }
 
 #ifdef DEBUG
 struct CompartmentCheckTracer : public JSTracer
 {
     Cell *src;
     JSGCTraceKind srcKind;
@@ -3930,22 +3934,22 @@ BeginSweepingZoneGroup(JSRuntime *rt)
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_DISCARD_CODE);
             zone->discardJitCode(&fop);
         }
 
         bool releaseTypes = ReleaseObservedTypes(rt);
         for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
             gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
-            c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode());
+            c->sweep(&fop, releaseTypes);
         }
 
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             gcstats::AutoSCC scc(rt->gcStats, rt->gcZoneGroupIndex);
-            zone->sweep(&fop, releaseTypes && !zone->isPreservingCode());
+            zone->sweep(&fop, releaseTypes);
         }
     }
 
     /*
      * Queue all GC things in all zones for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
@@ -5284,16 +5288,20 @@ js::ReleaseAllJITCode(FreeOp *fop)
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
             jit::FinishDiscardBaselineScript(fop, script);
         }
     }
+
+    /* Sweep now invalidated compiler outputs from each compartment. */
+    for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next())
+        comp->types.clearCompilerOutputs(fop);
 #endif
 }
 
 /*
  * There are three possible PCCount profiling states:
  *
  * 1. None: Neither scripts nor the runtime have count information.
  * 2. Profile: Active scripts have count information, the runtime does not.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1086,16 +1086,17 @@ struct GCMarker : public JSTracer {
      * the explicit tags to distinguish them when it cannot be deduced from
      * the context of push or pop operation.
      */
     enum StackTag {
         ValueArrayTag,
         ObjectTag,
         TypeTag,
         XmlTag,
+        ArenaTag,
         SavedValueArrayTag,
         IonCodeTag,
         LastTag = IonCodeTag
     };
 
     static const uintptr_t StackTagMask = 7;
 
     static void staticAsserts() {
@@ -1113,16 +1114,20 @@ struct GCMarker : public JSTracer {
     void start();
     void stop();
     void reset();
 
     void pushObject(ObjectImpl *obj) {
         pushTaggedPtr(ObjectTag, obj);
     }
 
+    void pushArenaList(gc::ArenaHeader *firstArena) {
+        pushTaggedPtr(ArenaTag, firstArena);
+    }
+
     void pushType(types::TypeObject *type) {
         pushTaggedPtr(TypeTag, type);
     }
 
     void pushIonCode(jit::IonCode *code) {
         pushTaggedPtr(IonCodeTag, code);
     }
 
@@ -1219,17 +1224,17 @@ struct GCMarker : public JSTracer {
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
     bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget &budget);
-    void processMarkStackOther(uintptr_t tag, uintptr_t addr);
+    void processMarkStackOther(SliceBudget &budget, uintptr_t tag, uintptr_t addr);
 
     void appendGrayRoot(void *thing, JSGCTraceKind kind);
 
     /* The color is only applied to objects and functions. */
     uint32_t color;
 
     mozilla::DebugOnly<bool> started;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -238,16 +238,24 @@ types::TypeHasProperty(JSContext *cx, Ty
      */
     if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) {
         id = IdToTypeId(id);
 
         /* Watch for properties which inference does not monitor. */
         if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
             return true;
 
+        /*
+         * If we called in here while resolving a type constraint, we may be in the
+         * middle of resolving a standard class and the type sets will not be updated
+         * until the outer TypeSet::add finishes.
+         */
+        if (cx->compartment()->types.pendingCount)
+            return true;
+
         Type type = GetValueType(value);
 
         AutoEnterAnalysis enter(cx);
 
         /*
          * We don't track types for properties inherited from prototypes which
          * haven't yet been accessed during analysis of the inheriting object.
          * Don't do the property instantiation now.
@@ -729,39 +737,30 @@ class TypeCompilerConstraint : public Ty
     TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data)
       : compilation(compilation), data(data)
     {}
 
     const char *kind() { return data.kind(); }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {
         if (data.invalidateOnNewType(type))
-            cx->zone()->types.addPendingRecompile(cx, compilation);
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 
     void newPropertyState(JSContext *cx, TypeSet *source) {
         if (data.invalidateOnNewPropertyState(source))
-            cx->zone()->types.addPendingRecompile(cx, compilation);
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 
     void newObjectState(JSContext *cx, TypeObject *object) {
         // Note: Once the object has unknown properties, no more notifications
         // will be sent on changes to its state, so always invalidate any
         // associated compilations.
         if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
-            cx->zone()->types.addPendingRecompile(cx, compilation);
-    }
-
-    TypeConstraint *sweep(TypeZone &zone) {
-        if (data.shouldSweep() || compilation.shouldSweep(zone))
-            return nullptr;
-        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
-        if (!res)
-            zone.setPendingNukeTypes();
-        return res;
+            cx->compartment()->types.addPendingRecompile(cx, compilation);
     }
 };
 
 template <typename T>
 bool
 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
 {
     if (property.object()->unknownProperties())
@@ -904,61 +903,46 @@ class TypeConstraintFreezeStack : public
 
   public:
     TypeConstraintFreezeStack(JSScript *script)
         : script_(script)
     {}
 
     const char *kind() { return "freezeStack"; }
 
-    void newType(JSContext *cx, TypeSet *source, Type type) {
+    void newType(JSContext *cx, TypeSet *source, Type type)
+    {
         /*
          * Unlike TypeConstraintFreeze, triggering this constraint once does
          * not disable it on future changes to the type set.
          */
-        cx->zone()->types.addPendingRecompile(cx, script_);
-    }
-
-    TypeConstraint *sweep(TypeZone &zone) {
-        if (IsScriptAboutToBeFinalized(&script_))
-            return nullptr;
-        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
-        if (!res)
-            zone.setPendingNukeTypes();
-        return res;
+        cx->compartment()->types.addPendingRecompile(cx, script_);
     }
 };
 
 } /* anonymous namespace */
 
 bool
 types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode,
                          CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
 {
     if (constraints->failed())
         return false;
 
     CompilerOutput co(script, executionMode);
 
-    TypeZone &types = cx->zone()->types;
-    if (!types.compilerOutputs) {
-        types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
-        if (!types.compilerOutputs)
+    TypeCompartment &types = cx->compartment()->types;
+    if (!types.constrainedOutputs) {
+        types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
+        if (!types.constrainedOutputs)
             return false;
     }
 
-#ifdef DEBUG
-    for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
-        const CompilerOutput &co = (*types.compilerOutputs)[i];
-        JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
-    }
-#endif
-
-    uint32_t index = types.compilerOutputs->length();
-    if (!types.compilerOutputs->append(co))
+    uint32_t index = types.constrainedOutputs->length();
+    if (!types.constrainedOutputs->append(co))
         return false;
 
     *precompileInfo = RecompileInfo(index);
 
     bool succeeded = true;
 
     for (size_t i = 0; i < constraints->length(); i++) {
         CompilerConstraint *constraint = constraints->get(i);
@@ -990,18 +974,18 @@ types::FinishCompilation(JSContext *cx, 
 
         size_t count = TypeScript::NumTypeSets(entry.script);
 
         StackTypeSet *array = entry.script->types->typeArray();
         for (size_t i = 0; i < count; i++)
             array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
     }
 
-    if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
-        types.compilerOutputs->back().invalidate();
+    if (!succeeded || types.constrainedOutputs->back().pendingInvalidation()) {
+        types.constrainedOutputs->back().invalidate();
         script->resetUseCount();
         return false;
     }
 
     return true;
 }
 
 namespace {
@@ -1020,18 +1004,16 @@ class ConstraintDataFreeze
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return expected
                ? property.maybeTypes()->isSubset(expected)
                : property.maybeTypes()->empty();
     }
-
-    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 void
 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
 {
     LifoAlloc *alloc = constraints->alloc();
@@ -1208,18 +1190,16 @@ class ConstraintDataFreezeObjectFlags
         return object->hasAnyFlags(flags);
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
-
-    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 bool
 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
     JS_ASSERT(flags);
@@ -1304,18 +1284,16 @@ class ConstraintDataFreezeObjectForInlin
         return true;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return true;
     }
-
-    bool shouldSweep() { return false; }
 };
 
 // Constraint which triggers recompilation when the template object for a
 // type's new script changes.
 class ConstraintDataFreezeObjectForNewScriptTemplate
 {
     JSObject *templateObject;
 
@@ -1332,21 +1310,16 @@ class ConstraintDataFreezeObjectForNewSc
         return !object->hasNewScript() || object->newScript()->templateObject != templateObject;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
-
-    bool shouldSweep() {
-        // Note: |templateObject| is only used for equality testing.
-        return false;
-    }
 };
 
 // Constraint which triggers recompilation when the underlying data pointer for
 // a typed array changes.
 class ConstraintDataFreezeObjectForTypedArrayBuffer
 {
     void *viewData;
 
@@ -1363,21 +1336,16 @@ class ConstraintDataFreezeObjectForTyped
         return object->singleton->as<TypedArrayObject>().viewData() != viewData;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
-
-    bool shouldSweep() {
-        // Note: |viewData| is only used for equality testing.
-        return false;
-    }
 };
 
 } /* anonymous namespace */
 
 void
 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
 {
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
@@ -1440,49 +1408,67 @@ ObjectStateChange(ExclusiveContext *cxAr
 static void
 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
 
 namespace {
 
 class ConstraintDataFreezeConfiguredProperty
 {
   public:
-    ConstraintDataFreezeConfiguredProperty()
+    TypeObjectKey *object;
+
+    ConstraintDataFreezeConfiguredProperty(TypeObjectKey *object)
+      : object(object)
     {}
 
     const char *kind() { return "freezeConfiguredProperty"; }
 
     bool invalidateOnNewType(Type type) { return false; }
     bool invalidateOnNewPropertyState(TypeSet *property) {
         return property->configuredProperty();
     }
     bool invalidateOnNewObjectState(TypeObject *object) { return false; }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
+        // Everywhere compiled code depends on definite properties associated
+        // with a type object's newScript, we need to make sure there are
+        // constraints in place which will mark those properties as configured
+        // should the definite properties be invalidated.
+        TypeObject *type = object->isSingleObject()
+                           ? object->asSingleObject()->type()
+                           : object->asTypeObject();
+        if (type->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
+            type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
+            if (type->hasNewScript()) {
+                CheckNewScriptProperties(cx, type, type->newScript()->fun);
+            } else {
+                JS_ASSERT(type->flags & OBJECT_FLAG_ADDENDUM_CLEARED);
+                type->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
+            }
+        }
+
         return !property.maybeTypes()->configuredProperty();
     }
-
-    bool shouldSweep() { return false; }
 };
 
 } /* anonymous namespace */
 
 bool
-HeapTypeSetKey::configured(CompilerConstraintList *constraints)
+HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
 {
     if (maybeTypes() && maybeTypes()->configuredProperty())
         return true;
 
     LifoAlloc *alloc = constraints->alloc();
 
     typedef CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> T;
     constraints->add(alloc->new_<T>(alloc, *this,
-                                    ConstraintDataFreezeConfiguredProperty()));
+                                    ConstraintDataFreezeConfiguredProperty(type)));
     return false;
 }
 
 bool
 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
 {
     if (other->unknown())
         return unknown();
@@ -1995,17 +1981,17 @@ PrototypeHasIndexedProperty(CompilerCons
 {
     do {
         TypeObjectKey *type = TypeObjectKey::get(obj);
         if (ClassCanHaveExtraProperties(type->clasp()))
             return true;
         if (type->unknownProperties())
             return true;
         HeapTypeSetKey index = type->property(JSID_VOID);
-        if (index.configured(constraints) || index.isOwnProperty(constraints))
+        if (index.configured(constraints, type) || index.isOwnProperty(constraints))
             return true;
         obj = obj->getProto();
     } while (obj);
 
     return false;
 }
 
 bool
@@ -2033,18 +2019,37 @@ types::TypeCanHaveExtraIndexedProperties
 
     JSObject *proto = types->getCommonPrototype();
     if (!proto)
         return true;
 
     return PrototypeHasIndexedProperty(constraints, proto);
 }
 
+bool
+TypeCompartment::growPendingArray(JSContext *cx)
+{
+    unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
+    PendingWork *newArray = js_pod_calloc<PendingWork>(newCapacity);
+    if (!newArray) {
+        cx->compartment()->types.setPendingNukeTypes(cx);
+        return false;
+    }
+
+    PodCopy(newArray, pendingArray, pendingCount);
+    js_free(pendingArray);
+
+    pendingArray = newArray;
+    pendingCapacity = newCapacity;
+
+    return true;
+}
+
 void
-TypeZone::processPendingRecompiles(FreeOp *fop)
+TypeCompartment::processPendingRecompiles(FreeOp *fop)
 {
     if (!pendingRecompiles)
         return;
 
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<RecompileInfo> *pending = pendingRecompiles;
     pendingRecompiles = nullptr;
 
@@ -2085,19 +2090,21 @@ TypeZone::nukeTypes(FreeOp *fop)
      * Because of the nature of constraint-based analysis (add constraints, and
      * iterate them until reaching a fixpoint), we can't undo an add of a type set,
      * and merely aborting the operation which triggered the add will not be
      * sufficient for correct behavior as we will be leaving the types in an
      * inconsistent state.
      */
     JS_ASSERT(pendingNukeTypes);
 
-    if (pendingRecompiles) {
-        fop->free_(pendingRecompiles);
-        pendingRecompiles = nullptr;
+    for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) {
+        if (comp->types.pendingRecompiles) {
+            fop->free_(comp->types.pendingRecompiles);
+            comp->types.pendingRecompiles = nullptr;
+        }
     }
 
     inferenceEnabled = false;
 
 #ifdef JS_ION
     jit::InvalidateAll(fop, zone());
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
@@ -2107,17 +2114,17 @@ TypeZone::nukeTypes(FreeOp *fop)
         jit::FinishInvalidation(fop, script);
     }
 #endif /* JS_ION */
 
     pendingNukeTypes = false;
 }
 
 void
-TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
+TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
     CompilerOutput *co = info.compilerOutput(cx);
     if (!co || !co->isValid() || co->pendingInvalidation())
         return;
 
     if (!pendingRecompiles) {
         pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
         if (!pendingRecompiles) {
@@ -2133,17 +2140,17 @@ TypeZone::addPendingRecompile(JSContext 
 
     InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
               co->script(), co->script()->filename(), co->script()->lineno());
 
     co->setPendingInvalidation();
 }
 
 void
-TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
+TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(script);
 
 #ifdef JS_ION
     CancelOffThreadIonCompile(cx->compartment(), script);
 
     // Let the script warm up again before attempting another compile.
     if (jit::IsBaselineEnabled(cx))
@@ -3134,25 +3141,16 @@ class TypeConstraintClearDefiniteGetterS
          * non-writable, both of which are indicated by the source type set
          * being marked as configured.
          */
         if (!(object->flags & OBJECT_FLAG_ADDENDUM_CLEARED) && source->configuredProperty())
             object->clearAddendum(cx);
     }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
-
-    TypeConstraint *sweep(TypeZone &zone) {
-        if (IsTypeObjectAboutToBeFinalized(&object))
-            return nullptr;
-        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
-        if (!res)
-            zone.setPendingNukeTypes();
-        return res;
-    }
 };
 
 bool
 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, jsid id)
 {
     /*
      * Ensure that if the properties named here could have a getter, setter or
      * a permanent property in any transitive prototype, the definite
@@ -3189,25 +3187,16 @@ class TypeConstraintClearDefiniteSingle 
 
     void newType(JSContext *cx, TypeSet *source, Type type) {
         if (object->flags & OBJECT_FLAG_ADDENDUM_CLEARED)
             return;
 
         if (source->baseFlags() || source->getObjectCount() > 1)
             object->clearAddendum(cx);
     }
-
-    TypeConstraint *sweep(TypeZone &zone) {
-        if (IsTypeObjectAboutToBeFinalized(&object))
-            return nullptr;
-        TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
-        if (!res)
-            zone.setPendingNukeTypes();
-        return res;
-    }
 };
 
 void
 types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
                                             JSScript *script, JSScript *calleeScript)
 {
     // Look for any uses of the specified calleeScript in type sets for
     // |script|, and add constraints to ensure that if the type sets' contents
@@ -3947,29 +3936,18 @@ ConstraintTypeSet::sweep(Zone *zone)
     } else if (objectCount == 1) {
         TypeObjectKey *object = (TypeObjectKey *) objectSet;
         if (IsAboutToBeFinalized(object)) {
             objectSet = nullptr;
             setBaseObjectCount(0);
         }
     }
 
-    /*
-     * Type constraints only hold weak references. Copy constraints referring
-     * to data that is still live into the zone's new arena.
-     */
-    TypeConstraint *constraint = constraintList;
+    /* All constraints are wiped out on each GC. */
     constraintList = nullptr;
-    while (constraint) {
-        if (TypeConstraint *copy = constraint->sweep(zone->types)) {
-            copy->next = constraintList;
-            constraintList = copy;
-        }
-        constraint = constraint->next;
-    }
 }
 
 inline void
 TypeObject::clearProperties()
 {
     setBasePropertyCount(0);
     propertySet = nullptr;
 }
@@ -3979,16 +3957,28 @@ TypeObject::clearProperties()
  * compartment to fixup weak references: property type sets referencing dead
  * JS and type objects, and singleton JS objects whose type is not referenced
  * elsewhere. This also releases memory associated with dead type objects,
  * so that type objects do not need later finalization.
  */
 inline void
 TypeObject::sweep(FreeOp *fop)
 {
+    if (singleton) {
+        JS_ASSERT(!hasNewScript());
+
+        /*
+         * All properties can be discarded. We will regenerate them as needed
+         * as code gets reanalyzed.
+         */
+        clearProperties();
+
+        return;
+    }
+
     if (!isMarked()) {
         if (addendum)
             fop->free_(addendum);
         return;
     }
 
     js::LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
 
@@ -4001,24 +3991,16 @@ TypeObject::sweep(FreeOp *fop)
         unsigned oldCapacity = HashSetCapacity(propertyCount);
         Property **oldArray = propertySet;
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property *prop = oldArray[i];
             if (prop) {
-                if (singleton && !prop->types.constraintList) {
-                    /*
-                     * Don't copy over properties of singleton objects which
-                     * don't have associated constraints. The contents of these
-                     * type sets will be regenerated as necessary.
-                     */
-                    continue;
-                }
                 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
                 if (newProp) {
                     Property **pentry =
                         HashSetInsert<jsid,Property,Property>
                             (typeLifoAlloc, propertySet, propertyCount, prop->id);
                     if (pentry) {
                         *pentry = newProp;
                         newProp->types.sweep(zone());
@@ -4028,34 +4010,37 @@ TypeObject::sweep(FreeOp *fop)
                 } else {
                     zone()->types.setPendingNukeTypes();
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
-        if (singleton && !prop->types.constraintList) {
-            // Skip, as above.
-            clearProperties();
+        Property *newProp = typeLifoAlloc.new_<Property>(*prop);
+        if (newProp) {
+            propertySet = (Property **) newProp;
+            newProp->types.sweep(zone());
         } else {
-            Property *newProp = typeLifoAlloc.new_<Property>(*prop);
-            if (newProp) {
-                propertySet = (Property **) newProp;
-                newProp->types.sweep(zone());
-            } else {
-                zone()->types.setPendingNukeTypes();
-            }
+            zone()->types.setPendingNukeTypes();
         }
     }
 
     if (basePropertyCount() <= SET_ARRAY_SIZE) {
         for (unsigned i = 0; i < basePropertyCount(); i++)
             JS_ASSERT(propertySet[i]);
     }
+
+    /*
+     * The GC will clear out the constraints ensuring the correctness of the
+     * newScript information, these constraints will need to be regenerated
+     * the next time we compile code which depends on this info.
+     */
+    if (hasNewScript())
+        flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
 }
 
 void
 TypeCompartment::sweep(FreeOp *fop)
 {
     /*
      * Iterate through the array/object type tables and remove all entries
      * referencing collected data. These tables only hold weak references.
@@ -4129,16 +4114,62 @@ TypeCompartment::sweep(FreeOp *fop)
             bool keyDying = IsScriptAboutToBeFinalized(&key.script);
             bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet());
             if (keyDying || valDying)
                 e.removeFront();
             else if (key.script != e.front().key().script)
                 e.rekeyFront(key);
         }
     }
+
+    /*
+     * The pending array is reset on GC, it can grow large (75+ KB) and is easy
+     * to reallocate if the compartment becomes active again.
+     */
+    if (pendingArray)
+        fop->free_(pendingArray);
+
+    pendingArray = nullptr;
+    pendingCapacity = 0;
+}
+
+void
+TypeCompartment::sweepShapes(FreeOp *fop)
+{
+    /*
+     * Sweep any weak shape references that may be finalized even if a GC is
+     * preserving type information.
+     */
+    if (objectTypeTable) {
+        for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
+            const ObjectTableKey &key = e.front().key();
+            ObjectTableEntry &entry = e.front().value();
+
+            if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) {
+                fop->free_(key.properties);
+                fop->free_(entry.types);
+                e.removeFront();
+            }
+        }
+    }
+}
+
+void
+TypeCompartment::clearCompilerOutputs(FreeOp *fop)
+{
+    if (constrainedOutputs) {
+        fop->delete_(constrainedOutputs);
+        constrainedOutputs = nullptr;
+    }
+
+    if (pendingRecompiles) {
+        JS_ASSERT(pendingRecompiles->length() == 0);
+        fop->delete_(pendingRecompiles);
+        pendingRecompiles = nullptr;
+    }
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
 {
     gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats,
                           gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT);
 
@@ -4157,16 +4188,17 @@ JSCompartment::sweepNewTypeObjectTable(T
                 e.rekeyFront(lookup, entry);
             }
         }
     }
 }
 
 TypeCompartment::~TypeCompartment()
 {
+    js_free(pendingArray);
     js_delete(arrayTypeTable);
     js_delete(objectTypeTable);
     js_delete(allocationSiteTable);
 }
 
 /* static */ void
 TypeScript::Sweep(FreeOp *fop, JSScript *script)
 {
@@ -4175,36 +4207,52 @@ TypeScript::Sweep(FreeOp *fop, JSScript 
     JS_ASSERT(compartment->zone()->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     StackTypeSet *typeArray = script->types->typeArray();
 
     /* Remove constraints and references to dead objects from the persistent type sets. */
     for (unsigned i = 0; i < num; i++)
         typeArray[i].sweep(compartment->zone());
+
+    /*
+     * Freeze constraints on stack type sets need to be regenerated the next
+     * time the script is analyzed.
+     */
+    script->clearHasFreezeConstraints();
 }
 
 void
 TypeScript::destroy()
 {
     js_free(this);
 }
 
 void
 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *typePool)
 {
     *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                        size_t *pendingArrays,
                                         size_t *allocationSiteTables,
                                         size_t *arrayTypeTables,
                                         size_t *objectTypeTables)
 {
+    /* Pending arrays are cleared on GC along with the analysis pool. */
+    *pendingArrays += mallocSizeOf(pendingArray);
+
+    /*
+     * TypeCompartment::pendingRecompiles is non-nullptr only while inference
+     * code is running.
+     */
+    JS_ASSERT(!pendingRecompiles);
+
     if (allocationSiteTable)
         *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
 
     if (arrayTypeTable)
         *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
 
     if (objectTypeTable) {
         *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
@@ -4236,84 +4284,55 @@ TypeObject::sizeOfExcludingThis(mozilla:
     }
 
     return mallocSizeOf(addendum);
 }
 
 TypeZone::TypeZone(Zone *zone)
   : zone_(zone),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    compilerOutputs(nullptr),
-    pendingRecompiles(nullptr),
     pendingNukeTypes(false),
     inferenceEnabled(false)
 {
 }
 
 TypeZone::~TypeZone()
 {
-    js_delete(compilerOutputs);
-    js_delete(pendingRecompiles);
 }
 
 void
 TypeZone::sweep(FreeOp *fop, bool releaseTypes)
 {
     JS_ASSERT(zone()->isGCSweeping());
 
     JSRuntime *rt = fop->runtime();
 
     /*
      * Clear the analysis pool, but don't release its data yet. While
      * sweeping types any live data will be allocated into the pool.
      */
     LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
     oldAlloc.steal(&typeLifoAlloc);
 
-    /* Sweep and find compressed indexes for each compiler output. */
-    size_t newCompilerOutputCount = 0;
-    if (compilerOutputs) {
-        for (size_t i = 0; i < compilerOutputs->length(); i++) {
-            CompilerOutput &output = (*compilerOutputs)[i];
-            if (output.isValid()) {
-                JSScript *script = output.script();
-                if (IsScriptAboutToBeFinalized(&script))
-                    output.invalidate();
-                else
-                    output.setSweepIndex(newCompilerOutputCount++);
-            }
-        }
-    }
-
+    /*
+     * Sweep analysis information and everything depending on it from the
+     * compartment, including all remaining mjit code if inference is
+     * enabled in the compartment.
+     */
     if (inferenceEnabled) {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
         for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (script->types) {
                 types::TypeScript::Sweep(fop, script);
 
                 if (releaseTypes) {
                     script->types->destroy();
                     script->types = nullptr;
-
-                    /*
-                     * Freeze constraints on stack type sets need to be
-                     * regenerated the next time the script is analyzed.
-                     */
-                    script->clearHasFreezeConstraints();
-
-                    JS_ASSERT(!script->hasIonScript());
-                    JS_ASSERT(!script->hasParallelIonScript());
-                } else {
-                    /* Update the recompile indexes in any IonScripts still on the script. */
-                    if (script->hasIonScript())
-                        script->ionScript()->recompileInfoRef().shouldSweep(*this);
-                    if (script->hasParallelIonScript())
-                        script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
                 }
             }
         }
     }
 
     {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
 
@@ -4323,30 +4342,16 @@ TypeZone::sweep(FreeOp *fop, bool releas
             TypeObject *object = iter.get<TypeObject>();
             object->sweep(fop);
         }
 
         for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
             comp->types.sweep(fop);
     }
 
-    if (compilerOutputs) {
-        size_t sweepIndex = 0;
-        for (size_t i = 0; i < compilerOutputs->length(); i++) {
-            CompilerOutput output = (*compilerOutputs)[i];
-            if (output.isValid()) {
-                JS_ASSERT(sweepIndex == output.sweepIndex());
-                output.setSweepIndex(0);
-                (*compilerOutputs)[sweepIndex++] = output;
-            }
-        }
-        JS_ASSERT(sweepIndex == newCompilerOutputCount);
-        JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
-    }
-
     {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
         for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             script->clearAnalysis();
         }
     }
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -172,17 +172,17 @@ namespace jit {
 }
 
 namespace analyze {
     class ScriptAnalysis;
 }
 
 namespace types {
 
-class TypeZone;
+class TypeCompartment;
 class TypeSet;
 class TypeObjectKey;
 
 /*
  * Information about a single concrete type. We pack this into a single word,
  * where small values are particular primitive or other singleton types, and
  * larger values are either specific JS objects or type objects.
  */
@@ -273,20 +273,38 @@ class Type
 inline Type GetValueType(const Value &val);
 
 /*
  * Type inference memory management overview.
  *
  * Type information about the values observed within scripts and about the
  * contents of the heap is accumulated as the program executes. Compilation
  * accumulates constraints relating type information on the heap with the
- * compilations that should be invalidated when those types change. Type
- * information and constraints are allocated in the zone's typeLifoAlloc,
- * and on GC all data referring to live things is copied into a new allocator.
- * Thus, type set and constraints only hold weak references.
+ * compilations that should be invalidated when those types change. This data
+ * is periodically cleared to reduce memory usage.
+ *
+ * GCs may clear both analysis information and jitcode. Sometimes GCs will
+ * preserve all information and code, and will not collect any scripts, type
+ * objects or singleton JS objects.
+ *
+ * The following data is cleared by all non-preserving GCs:
+ *
+ * - The ScriptAnalysis for each analyzed script and data from each analysis
+ *   pass performed.
+ *
+ * - Property type sets for singleton JS objects.
+ *
+ * - Type constraints and dead references in all type sets.
+ *
+ * The following data is occasionally cleared by non-preserving GCs:
+ *
+ * - TypeScripts and their type sets are occasionally destroyed, per a timer.
+ *
+ * - When a JSScript or TypeObject is swept, type information for its contents
+ *   is destroyed.
  */
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */
 class TypeConstraint
 {
@@ -311,22 +329,16 @@ public:
     virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
 
     /*
      * For constraints attached to the JSID_EMPTY type set on an object,
      * indicate a change in one of the object's dynamic property flags or other
      * state.
      */
     virtual void newObjectState(JSContext *cx, TypeObject *object) {}
-
-    /*
-     * If the data this constraint refers to is still live, copy it into the
-     * zone's new allocator. Type constraints only hold weak references.
-     */
-    virtual TypeConstraint *sweep(TypeZone &zone) = 0;
 };
 
 /* Flags and other state stored in TypeSet::flags */
 enum {
     TYPE_FLAG_UNDEFINED =  0x1,
     TYPE_FLAG_NULL      =  0x2,
     TYPE_FLAG_BOOLEAN   =  0x4,
     TYPE_FLAG_INT32     =  0x8,
@@ -378,16 +390,23 @@ typedef uint32_t TypeFlags;
 enum {
     /* Whether this type object is associated with some allocation site. */
     OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
 
     /* If set, addendum information should not be installed on this object. */
     OBJECT_FLAG_ADDENDUM_CLEARED      = 0x2,
 
     /*
+     * If set, type constraints covering the correctness of the newScript
+     * definite properties need to be regenerated before compiling any jitcode
+     * which depends on this information.
+     */
+    OBJECT_FLAG_NEW_SCRIPT_REGENERATE = 0x4,
+
+    /*
      * Whether we have ensured all type sets in the compartment contain
      * ANYOBJECT instead of this object.
      */
     OBJECT_FLAG_SETS_MARKED_UNKNOWN   = 0x8,
 
     /* Mask/shift for the number of properties in propertySet */
     OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff0,
     OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 4,
@@ -845,16 +864,22 @@ struct TypeTypedObject : public TypeObje
  * We can recover the type information for the object from examining it,
  * so don't normally track the possible types of its properties as it is
  * updated. Property type sets for the object are only constructed when an
  * analyzed script attaches constraints to it: the script is querying that
  * property off the object or another which delegates to it, and the analysis
  * information is sensitive to changes in the property's type. Future changes
  * to the property (whether those uncovered by analysis or those occurring
  * in the VM) will treat these properties like those of any other type object.
+ *
+ * When a GC occurs, we wipe out all analysis information for all the
+ * compartment's scripts, so can destroy all properties on singleton type
+ * objects at the same time. If there is no reference on the stack to the
+ * type object itself, the type object is also destroyed, and the JS object
+ * reverts to having a lazy type.
  */
 
 /* Type information about an object accessed by a script. */
 struct TypeObject : gc::BarrieredCell<TypeObject>
 {
     /* Class shared by objects using this type. */
     const Class *clasp;
 
@@ -1286,17 +1311,17 @@ class HeapTypeSetKey
     TypeObjectKey *object() const { return object_; }
     jsid id() const { return id_; }
     HeapTypeSet *maybeTypes() const { return maybeTypes_; }
 
     bool instantiate(JSContext *cx);
 
     void freeze(CompilerConstraintList *constraints);
     JSValueType knownTypeTag(CompilerConstraintList *constraints);
-    bool configured(CompilerConstraintList *constraints);
+    bool configured(CompilerConstraintList *constraints, TypeObjectKey *type);
     bool isOwnProperty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);
     bool needsBarrier(CompilerConstraintList *constraints);
 };
 
 /*
  * Information about the result of the compilation of a script.  This structure
@@ -1309,20 +1334,16 @@ class CompilerOutput
     // If this compilation has not been invalidated, the associated script and
     // kind of compilation being performed.
     JSScript *script_;
     ExecutionMode mode_ : 2;
 
     // Whether this compilation is about to be invalidated.
     bool pendingInvalidation_ : 1;
 
-    // During sweeping, the list of compiler outputs is compacted and invalidated
-    // outputs are removed. This gives the new index for a valid compiler output.
-    uint32_t sweepIndex_ : 29;
-
   public:
     CompilerOutput()
       : script_(nullptr), mode_(SequentialExecution), pendingInvalidation_(false)
     {}
 
     CompilerOutput(JSScript *script, ExecutionMode mode)
       : script_(script), mode_(mode), pendingInvalidation_(false)
     {}
@@ -1340,52 +1361,65 @@ class CompilerOutput
     }
 
     void setPendingInvalidation() {
         pendingInvalidation_ = true;
     }
     bool pendingInvalidation() {
         return pendingInvalidation_;
     }
-
-    void setSweepIndex(uint32_t index) {
-        if (index >= 1 << 29)
-            MOZ_CRASH();
-        sweepIndex_ = index;
-    }
-    uint32_t sweepIndex() {
-        return sweepIndex_;
-    }
 };
 
 class RecompileInfo
 {
     uint32_t outputIndex;
 
   public:
     RecompileInfo(uint32_t outputIndex = uint32_t(-1))
       : outputIndex(outputIndex)
     {}
 
     bool operator == (const RecompileInfo &o) const {
         return outputIndex == o.outputIndex;
     }
-    CompilerOutput *compilerOutput(TypeZone &types) const;
+    CompilerOutput *compilerOutput(TypeCompartment &types) const;
     CompilerOutput *compilerOutput(JSContext *cx) const;
-    bool shouldSweep(TypeZone &types);
 };
 
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /* Constraint solving worklist structures. */
 
+    /*
+     * Worklist of types which need to be propagated to constraints. We use a
+     * worklist to avoid blowing the native stack.
+     */
+    struct PendingWork
+    {
+        TypeConstraint *constraint;
+        ConstraintTypeSet *source;
+        Type type;
+    };
+    PendingWork *pendingArray;
+    unsigned pendingCount;
+    unsigned pendingCapacity;
+
+    /* Whether we are currently resolving the pending worklist. */
+    bool resolving;
+
     /* Number of scripts in this compartment. */
     unsigned scriptCount;
 
+    /* Valid & Invalid script referenced by type constraints. */
+    Vector<CompilerOutput> *constrainedOutputs;
+
+    /* Pending recompilations to perform before execution of JIT code can resume. */
+    Vector<RecompileInfo> *pendingRecompiles;
+
     /* Table for referencing types of objects keyed to an allocation site. */
     AllocationSiteTable *allocationSiteTable;
 
     /* Tables for determining types of singleton/JSON objects. */
 
     ArrayTypeTable *arrayTypeTable;
     ObjectTypeTable *objectTypeTable;
 
@@ -1399,67 +1433,75 @@ struct TypeCompartment
 
     JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
 
     TypeCompartment();
     ~TypeCompartment();
 
     inline JSCompartment *compartment();
 
+    /* Add a type to register with a list of constraints. */
+    inline void addPending(JSContext *cx, TypeConstraint *constraint,
+                           ConstraintTypeSet *source, Type type);
+    bool growPendingArray(JSContext *cx);
+
+    /* Resolve pending type registrations, excluding delayed ones. */
+    inline void resolvePending(JSContext *cx);
+
     /* Prints results of this compartment if spew is enabled or force is set. */
     void print(JSContext *cx, bool force);
 
     /*
      * Make a function or non-function object associated with an optional
      * script. The 'key' parameter here may be an array, typed array, function
      * or JSProto_Object to indicate a type whose class is unknown (not just
      * js_ObjectClass).
      */
     TypeObject *newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
                               bool unknown = false);
 
     /* Get or make an object for an allocation site, and add to the allocation site table. */
     TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
 
+    void processPendingRecompiles(FreeOp *fop);
+
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(ExclusiveContext *cx);
 
+    /* Mark a script as needing recompilation once inference has finished. */
+    void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
+    void addPendingRecompile(JSContext *cx, JSScript *script);
+
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
+    void sweepShapes(FreeOp *fop);
+    void clearCompilerOutputs(FreeOp *fop);
+
     void finalizeObjects();
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                size_t *pendingArrays,
                                 size_t *allocationSiteTables,
                                 size_t *arrayTypeTables,
                                 size_t *objectTypeTables);
 };
 
 void FixRestArgumentsType(ExclusiveContext *cxArg, JSObject *obj);
 
 struct TypeZone
 {
     JS::Zone                     *zone_;
 
     /* Pool for type information in this zone. */
     static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
     js::LifoAlloc                typeLifoAlloc;
 
     /*
-     * All Ion compilations that have occured in this zone, for indexing via
-     * RecompileInfo. This includes both valid and invalid compilations, though
-     * invalidated compilations are swept on GC.
-     */
-    Vector<CompilerOutput> *compilerOutputs;
-
-    /* Pending recompilations to perform before execution of JIT code can resume. */
-    Vector<RecompileInfo> *pendingRecompiles;
-
-    /*
      * Bit set if all current types must be marked as unknown, and all scripts
      * recompiled. Caused by OOM failure within inference operations.
      */
     bool                         pendingNukeTypes;
 
     /* Whether type inference is enabled in this compartment. */
     bool                         inferenceEnabled;
 
@@ -1469,22 +1511,16 @@ struct TypeZone
 
     JS::Zone *zone() const { return zone_; }
 
     void sweep(FreeOp *fop, bool releaseTypes);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes();
 
-    /* Mark a script as needing recompilation once inference has finished. */
-    void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
-    void addPendingRecompile(JSContext *cx, JSScript *script);
-
-    void processPendingRecompiles(FreeOp *fop);
-
     void nukeTypes(FreeOp *fop);
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -99,39 +99,27 @@ CompilerOutput::ion() const
     jit::IonScript *ion = jit::GetIonScript(script(), mode());
     JS_ASSERT(ion != ION_COMPILING_SCRIPT);
     return ion;
 #endif
     MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput");
 }
 
 inline CompilerOutput*
-RecompileInfo::compilerOutput(TypeZone &types) const
+RecompileInfo::compilerOutput(TypeCompartment &types) const
 {
-    if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
+    if (!types.constrainedOutputs || outputIndex >= types.constrainedOutputs->length())
         return nullptr;
-    return &(*types.compilerOutputs)[outputIndex];
+    return &(*types.constrainedOutputs)[outputIndex];
 }
 
 inline CompilerOutput*
 RecompileInfo::compilerOutput(JSContext *cx) const
 {
-    return compilerOutput(cx->zone()->types);
-}
-
-inline bool
-RecompileInfo::shouldSweep(TypeZone &types)
-{
-    CompilerOutput *output = compilerOutput(types);
-    if (!output || !output->isValid())
-        return true;
-
-    // Update this info for the output's new index in the zone's compiler outputs.
-    outputIndex = output->sweepIndex();
-    return false;
+    return compilerOutput(cx->compartment()->types);
 }
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline Type
 Type::ObjectType(JSObject *obj)
@@ -295,23 +283,24 @@ struct AutoEnterAnalysis
     ~AutoEnterAnalysis()
     {
         compartment->activeAnalysis = oldActiveAnalysis;
 
         /*
          * If there are no more type inference activations on the stack,
          * process any triggered recompilations. Note that we should not be
          * invoking any scripted code while type inference is running.
+         * :TODO: assert this.
          */
         if (!compartment->activeAnalysis) {
-            TypeZone &types = compartment->zone()->types;
-            if (types.pendingNukeTypes)
-                types.nukeTypes(freeOp);
-            else if (types.pendingRecompiles)
-                types.processPendingRecompiles(freeOp);
+            TypeCompartment *types = &compartment->types;
+            if (compartment->zone()->types.pendingNukeTypes)
+                compartment->zone()->types.nukeTypes(freeOp);
+            else if (types->pendingRecompiles)
+                types->processPendingRecompiles(freeOp);
         }
     }
 
   private:
     void init(FreeOp *fop, JSCompartment *comp) {
         freeOp = fop;
         compartment = comp;
         oldActiveAnalysis = compartment->activeAnalysis;
@@ -858,16 +847,60 @@ TypeScript::SetArgument(JSContext *cx, J
 /////////////////////////////////////////////////////////////////////
 
 inline JSCompartment *
 TypeCompartment::compartment()
 {
     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
 }
 
+inline void
+TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint,
+                            ConstraintTypeSet *source, Type type)
+{
+    JS_ASSERT(this == &cx->compartment()->types);
+    JS_ASSERT(!cx->runtime()->isHeapBusy());
+
+    InferSpew(ISpewOps, "pending: %sC%p%s %s",
+              InferSpewColor(constraint), constraint, InferSpewColorReset(),
+              TypeString(type));
+
+    if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
+        return;
+
+    PendingWork &pending = pendingArray[pendingCount++];
+    pending.constraint = constraint;
+    pending.source = source;
+    pending.type = type;
+}
+
+inline void
+TypeCompartment::resolvePending(JSContext *cx)
+{
+    JS_ASSERT(this == &cx->compartment()->types);
+
+    if (resolving) {
+        /* There is an active call further up resolving the worklist. */
+        return;
+    }
+
+    resolving = true;
+
+    /* Handle all pending type registrations. */
+    while (pendingCount) {
+        const PendingWork &pending = pendingArray[--pendingCount];
+        InferSpew(ISpewOps, "resolve: %sC%p%s %s",
+                  InferSpewColor(pending.constraint), pending.constraint,
+                  InferSpewColorReset(), TypeString(pending.type));
+        pending.constraint->newType(cx, pending.source, pending.type);
+    }
+
+    resolving = false;
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
 /*
  * The sets of objects and scripts in a type set grow monotonically, are usually
  * empty, almost always small, and sometimes big.  For empty or singleton sets,
  * the pointer refers directly to the value.  For sets fitting into SET_ARRAY_SIZE,
@@ -1181,19 +1214,20 @@ ConstraintTypeSet::addType(ExclusiveCont
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type));
 
     /* Propagate the type to all constraints. */
     if (JSContext *cx = cxArg->maybeJSContext()) {
         TypeConstraint *constraint = constraintList;
         while (constraint) {
-            constraint->newType(cx, this, type);
+            cx->compartment()->types.addPending(cx, constraint, this, type);
             constraint = constraint->next;
         }
+        cx->compartment()->types.resolvePending(cx);
     } else {
         JS_ASSERT(!constraintList);
     }
 }
 
 inline void
 HeapTypeSet::setConfiguredProperty(ExclusiveContext *cxArg)
 {
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -210,16 +210,17 @@ StatsCompartmentCallback(JSRuntime *rt, 
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
     rtStats->initExtraCompartmentStats(compartment, &cStats);
 
     compartment->compartmentStats = &cStats;
 
     // Measure the compartment object itself, and things hanging off it.
     compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
+                                        &cStats.typeInferencePendingArrays,
                                         &cStats.typeInferenceAllocationSiteTables,
                                         &cStats.typeInferenceArrayTypeTables,
                                         &cStats.typeInferenceObjectTypeTables,
                                         &cStats.compartmentObject,
                                         &cStats.shapesMallocHeapCompartmentTables,
                                         &cStats.crossCompartmentWrappersTable,
                                         &cStats.regexpCompartment,
                                         &cStats.debuggeesSet,
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2117,16 +2117,20 @@ ReportCompartmentStats(const JS::Compart
                    cStats.ionData,
                    "Memory used by the IonMonkey JIT for compilation data: "
                    "IonScripts.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
                    cStats.typeInferenceTypeScripts,
                    "Memory used by type sets associated with scripts.");
 
+    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/pending-arrays"),
+                   cStats.typeInferencePendingArrays,
+                   "Memory used for solving constraints during type inference.");
+
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
                    cStats.typeInferenceAllocationSiteTables,
                    "Memory indexing type objects associated with allocation sites.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
                    cStats.typeInferenceArrayTypeTables,
                    "Memory indexing type objects associated with array literals.");