Bug 737365 - stop using the cx during finalization, part 3.
authorIgor Bukanov <igor@mir2.org>
Tue, 20 Mar 2012 11:22:05 +0100
changeset 94311 d8c5316f513afee907aabd5a04873bdf41fdebb7
parent 94310 74053b148a3c8883ad1a375107c94359606f6e1e
child 94312 82c7bdff95d4abc73c071f0dd901bde35b9b23f4
push idunknown
push userunknown
push dateunknown
bugs737365
milestone14.0a1
Bug 737365 - stop using the cx during finalization, part 3. This part removes the usage of JSContext* during the finalization and when sweeping the compartments. That required to change quite a few methods in type inference, jit and debugger implementation to take a FreeOp rather than JSContext pointer. In turn that also often required to replace cx->compartment usage with extracting the compartment from the passed objects or pass the compartment explicitly. On the plus side it allowed to remove fallible compartment enter code in methods that could be called during finalization.
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsprobes.cpp
js/src/jsprobes.h
js/src/jsprvtd.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsxml.cpp
js/src/jsxml.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/Retcon.cpp
js/src/methodjit/Retcon.h
js/src/methodjit/StubCalls.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GlobalObject.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -702,17 +702,17 @@ JSRuntime::JSRuntime()
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
     nativeStackBase(0),
     nativeStackQuota(0),
     interpreterFrames(NULL),
     cxCallback(NULL),
-    compartmentCallback(NULL),
+    destroyCompartmentCallback(NULL),
     activityCallback(NULL),
     activityCallbackArg(NULL),
 #ifdef JS_THREADSAFE
     suspendCount(0),
     requestDepth(0),
 # ifdef DEBUG
     checkRequestDepth(0),
 # endif
@@ -771,17 +771,17 @@ JSRuntime::JSRuntime()
     debugMode(false),
     profilingScripts(false),
     hadOutOfMemory(false),
     data(NULL),
 #ifdef JS_THREADSAFE
     gcLock(NULL),
     gcHelperThread(thisFromCtor()),
 #endif
-    defaultFreeOp_(thisFromCtor(), false, false, NULL),
+    defaultFreeOp_(thisFromCtor(), false, false),
     debuggerMutations(0),
     securityCallbacks(const_cast<JSSecurityCallbacks *>(&NullSecurityCallbacks)),
     destroyPrincipals(NULL),
     structuredCloneCallbacks(NULL),
     telemetryCallback(NULL),
     propertyRemovals(0),
     thousandsSeparator(0),
     decimalSeparator(0),
@@ -1320,22 +1320,20 @@ JS_SetJitHardening(JSRuntime *rt, JSBool
 }
 
 JS_PUBLIC_API(const char *)
 JS_GetImplementationVersion(void)
 {
     return "JavaScript-C 1.8.5+ 2011-04-16";
 }
 
-JS_PUBLIC_API(JSCompartmentCallback)
-JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback)
-{
-    JSCompartmentCallback old = rt->compartmentCallback;
-    rt->compartmentCallback = callback;
-    return old;
+JS_PUBLIC_API(void)
+JS_SetDestroyCompartmentCallback(JSRuntime *rt, JSDestroyCompartmentCallback callback)
+{
+    rt->destroyCompartmentCallback = callback;
 }
 
 JS_PUBLIC_API(JSWrapObjectCallback)
 JS_SetWrapObjectCallbacks(JSRuntime *rt,
                           JSWrapObjectCallback callback,
                           JSPreWrapCallback precallback)
 {
     JSWrapObjectCallback old = rt->wrapObjectCallback;
@@ -3047,17 +3045,17 @@ JS_IdArrayGet(JSContext *cx, JSIdArray *
 {
     JS_ASSERT(index >= 0 && index < ida->length);
     return ida->vector[index];
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
 {
-    cx->free_(ida);
+    DestroyIdArray(cx->runtime->defaultFreeOp(), ida);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToId(JSContext *cx, jsval v, jsid *idp)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
@@ -4271,17 +4269,17 @@ prop_iter_finalize(FreeOp *fop, JSObject
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
     if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
         /* Non-native case: destroy the ida enumerated when obj was created. */
         JSIdArray *ida = (JSIdArray *) pdata;
-        JS_DestroyIdArray(fop->context, ida);
+        DestroyIdArray(fop, ida);
     }
 }
 
 static void
 prop_iter_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1592,22 +1592,18 @@ typedef JSObject *
 /*
  * Callback used by the wrap hook to ask the embedding to prepare an object
  * for wrapping in a context. This might include unwrapping other wrappers
  * or even finding a more suitable object for the new compartment.
  */
 typedef JSObject *
 (* JSPreWrapCallback)(JSContext *cx, JSObject *scope, JSObject *obj, unsigned flags);
 
-typedef enum {
-    JSCOMPARTMENT_DESTROY
-} JSCompartmentOp;
-
-typedef JSBool
-(* JSCompartmentCallback)(JSContext *cx, JSCompartment *compartment, unsigned compartmentOp);
+typedef void
+(* JSDestroyCompartmentCallback)(JSFreeOp *fop, JSCompartment *compartment);
 
 /*
  * Read structured data from the reader r. This hook is used to read a value
  * previously serialized by a call to the WriteStructuredCloneOp hook.
  *
  * tag and data are the pair of uint32_t values from the header. The callback
  * may use the JS_Read* APIs to read any other relevant parts of the object
  * from the reader r. closure is any value passed to the JS_ReadStructuredClone
@@ -2676,18 +2672,18 @@ extern JS_PUBLIC_API(uint32_t)
 JS_ToggleOptions(JSContext *cx, uint32_t options);
 
 extern JS_PUBLIC_API(void)
 JS_SetJitHardening(JSRuntime *rt, JSBool enabled);
 
 extern JS_PUBLIC_API(const char *)
 JS_GetImplementationVersion(void);
 
-extern JS_PUBLIC_API(JSCompartmentCallback)
-JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback);
+extern JS_PUBLIC_API(void)
+JS_SetDestroyCompartmentCallback(JSRuntime *rt, JSDestroyCompartmentCallback callback);
 
 extern JS_PUBLIC_API(JSWrapObjectCallback)
 JS_SetWrapObjectCallbacks(JSRuntime *rt,
                           JSWrapObjectCallback callback,
                           JSPreWrapCallback precallback);
 
 extern JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -213,17 +213,17 @@ js_FinishAtomState(JSRuntime *rt)
     if (!state->atoms.initialized()) {
         /*
          * We are called with uninitialized state when JS_NewRuntime fails and
          * calls JS_DestroyRuntime on a partially initialized runtime.
          */
         return;
     }
 
-    FreeOp fop(rt, false, false, NULL);
+    FreeOp fop(rt, false, false);
     for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
         r.front().asPtr()->finalize(&fop);
 }
 
 bool
 js_InitCommonAtoms(JSContext *cx)
 {
     JSAtomState *state = &cx->runtime->atomState;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -181,28 +181,26 @@ struct ConservativeGCData
     bool hasStackToScan() const {
         return !!nativeStackTop;
     }
 };
 
 class FreeOp : public JSFreeOp {
     bool        shouldFreeLater_;
     bool        onBackgroundThread_;
+
   public:
-    JSContext   *context;
-
     static FreeOp *get(JSFreeOp *fop) {
         return static_cast<FreeOp *>(fop);
     }
 
-    FreeOp(JSRuntime *rt, bool shouldFreeLater, bool onBackgroundThread, JSContext *cx)
+    FreeOp(JSRuntime *rt, bool shouldFreeLater, bool onBackgroundThread)
       : JSFreeOp(rt),
         shouldFreeLater_(shouldFreeLater),
-        onBackgroundThread_(onBackgroundThread),
-        context(cx)
+        onBackgroundThread_(onBackgroundThread)
     {
     }
 
     bool shouldFreeLater() const {
         return shouldFreeLater_;
     }
 
     bool onBackgroundThread() const {
@@ -285,18 +283,18 @@ struct JSRuntime : js::RuntimeFriendFiel
      * Frames currently running in js::Interpret. See InterpreterFrames for
      * details.
      */
     js::InterpreterFrames *interpreterFrames;
 
     /* Context create/destroy callback. */
     JSContextCallback   cxCallback;
 
-    /* Compartment create/destroy callback. */
-    JSCompartmentCallback compartmentCallback;
+    /* Compartment destroy callback. */
+    JSDestroyCompartmentCallback destroyCompartmentCallback;
 
     js::ActivityCallback  activityCallback;
     void                 *activityCallbackArg;
 
 #ifdef JS_THREADSAFE
     /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */
     unsigned            suspendCount;
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -93,17 +93,17 @@ GetGSNCache(JSContext *cx)
 class AutoNamespaceArray : protected AutoGCRooter {
   public:
     AutoNamespaceArray(JSContext *cx)
         : AutoGCRooter(cx, NAMESPACES), context(cx) {
         array.init();
     }
 
     ~AutoNamespaceArray() {
-        array.finish(context);
+        array.finish(context->runtime->defaultFreeOp());
     }
 
     uint32_t length() const { return array.length; }
 
   private:
     JSContext *context;
     friend void AutoGCRooter::trace(JSTracer *trc);
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -461,28 +461,28 @@ JSCompartment::markTypes(JSTracer *trc)
     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
-JSCompartment::discardJitCode(JSContext *cx)
+JSCompartment::discardJitCode(FreeOp *fop)
 {
     /*
      * Kick all frames on the stack into the interpreter, and release all JIT
      * code in the compartment.
      */
 #ifdef JS_METHODJIT
     mjit::ClearAllFrames(this);
 
     for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        mjit::ReleaseScriptCode(cx, script);
+        mjit::ReleaseScriptCode(fop, script);
 
         /*
          * Use counts for scripts are reset on GC. After discarding code we
          * need to let it warm back up to get information like which opcodes
          * are setting array holes or accessing getter properties.
          */
         script->resetUseCount();
     }
@@ -516,17 +516,17 @@ JSCompartment::sweep(FreeOp *fop, bool r
         emptyTypeObject = NULL;
 
     newObjectCache.reset();
 
     sweepBreakpoints(fop);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
-        discardJitCode(fop->context);
+        discardJitCode(fop);
     }
 
     if (!activeAnalysis) {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
 
         /*
          * Clear the analysis pool, but don't release its data yet. While
          * sweeping types any live data will be allocated into the pool.
@@ -547,30 +547,30 @@ JSCompartment::sweep(FreeOp *fop, bool r
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->types) {
-                    types::TypeScript::Sweep(fop->context, script);
+                    types::TypeScript::Sweep(fop, script);
 
                     if (releaseTypes) {
                         script->types->destroy();
                         script->types = NULL;
                         script->typesPurged = true;
                     }
                 }
             }
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
-            types.sweep(fop->context);
+            types.sweep(fop);
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 script->clearAnalysis();
             }
@@ -672,22 +672,22 @@ JSCompartment::setDebugModeFromC(JSConte
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
             return false;
         }
     }
 
     debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
     JS_ASSERT(debugMode() == enabledAfter);
     if (enabledBefore != enabledAfter)
-        updateForDebugMode(cx);
+        updateForDebugMode(cx->runtime->defaultFreeOp());
     return true;
 }
 
 void
-JSCompartment::updateForDebugMode(JSContext *cx)
+JSCompartment::updateForDebugMode(FreeOp *fop)
 {
     for (ContextIter acx(rt); !acx.done(); acx.next()) {
         if (acx->compartment == this) 
             acx->updateJITEnabled();
     }
 
 #ifdef JS_METHODJIT
     bool enabled = debugMode();
@@ -699,17 +699,17 @@ JSCompartment::updateForDebugMode(JSCont
 
     /*
      * Discard JIT code and bytecode analyses for any scripts that change
      * debugMode.
      */
     for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->debugMode != enabled) {
-            mjit::ReleaseScriptCode(cx, script);
+            mjit::ReleaseScriptCode(fop, script);
             script->clearAnalysis();
             script->debugMode = enabled;
         }
     }
 #endif
 }
 
 bool
@@ -717,36 +717,36 @@ JSCompartment::addDebuggee(JSContext *cx
 {
     bool wasEnabled = debugMode();
     if (!debuggees.put(global)) {
         js_ReportOutOfMemory(cx);
         return false;
     }
     debugModeBits |= DebugFromJS;
     if (!wasEnabled)
-        updateForDebugMode(cx);
+        updateForDebugMode(cx->runtime->defaultFreeOp());
     return true;
 }
 
 void
-JSCompartment::removeDebuggee(JSContext *cx,
+JSCompartment::removeDebuggee(FreeOp *fop,
                               js::GlobalObject *global,
                               js::GlobalObjectSet::Enum *debuggeesEnum)
 {
     bool wasEnabled = debugMode();
     JS_ASSERT(debuggees.has(global));
     if (debuggeesEnum)
         debuggeesEnum->removeFront();
     else
         debuggees.remove(global);
 
     if (debuggees.empty()) {
         debugModeBits &= ~DebugFromJS;
         if (wasEnabled && !debugMode())
-            updateForDebugMode(cx);
+            updateForDebugMode(fop);
     }
 }
 
 void
 JSCompartment::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler)
 {
     for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
@@ -756,17 +756,17 @@ JSCompartment::clearBreakpointsIn(JSCont
 }
 
 void
 JSCompartment::clearTraps(JSContext *cx)
 {
     for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->hasAnyBreakpointsOrStepMode())
-            script->clearTraps(cx);
+            script->clearTraps(cx->runtime->defaultFreeOp());
     }
 }
 
 void
 JSCompartment::sweepBreakpoints(FreeOp *fop)
 {
     if (JS_CLIST_IS_EMPTY(&rt->debuggerList))
         return;
@@ -781,17 +781,17 @@ JSCompartment::sweepBreakpoints(FreeOp *
             if (!site)
                 continue;
             // nextbp is necessary here to avoid possibly reading *bp after
             // destroying it.
             Breakpoint *nextbp;
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
                 if (scriptGone || IsAboutToBeFinalized(bp->debugger->toJSObject()))
-                    bp->destroy(fop->context);
+                    bp->destroy(fop);
             }
         }
     }
 }
 
 size_t
 JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -378,17 +378,17 @@ struct JSCompartment
     bool wrap(JSContext *cx, JSObject **objp);
     bool wrapId(JSContext *cx, jsid *idp);
     bool wrap(JSContext *cx, js::PropertyOp *op);
     bool wrap(JSContext *cx, js::StrictPropertyOp *op);
     bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
 
     void markTypes(JSTracer *trc);
-    void discardJitCode(JSContext *cx);
+    void discardJitCode(js::FreeOp *fop);
     void sweep(js::FreeOp *fop, bool releaseTypes);
     void purge();
 
     void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(size_t amount);
 
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
@@ -441,22 +441,22 @@ struct JSCompartment
      */
     bool debugMode() const { return !!debugModeBits; }
 
     /* True if any scripts from this compartment are on the JS stack. */
     bool hasScriptsOnStack();
 
   private:
     /* This is called only when debugMode() has just toggled. */
-    void updateForDebugMode(JSContext *cx);
+    void updateForDebugMode(js::FreeOp *fop);
 
   public:
     js::GlobalObjectSet &getDebuggees() { return debuggees; }
     bool addDebuggee(JSContext *cx, js::GlobalObject *global);
-    void removeDebuggee(JSContext *cx, js::GlobalObject *global,
+    void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
                         js::GlobalObjectSet::Enum *debuggeesEnum = NULL);
     bool setDebugModeFromC(JSContext *cx, bool b);
 
     void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler);
     void clearTraps(JSContext *cx);
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -199,38 +199,38 @@ JS_SetTrap(JSContext *cx, JSScript *scri
     assertSameCompartment(cx, script, closure);
 
     if (!CheckDebugMode(cx))
         return false;
 
     BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, NULL);
     if (!site)
         return false;
-    site->setTrap(cx, handler, closure);
+    site->setTrap(cx->runtime->defaultFreeOp(), handler, closure);
     return true;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
              JSTrapHandler *handlerp, jsval *closurep)
 {
     if (BreakpointSite *site = script->getBreakpointSite(pc)) {
-        site->clearTrap(cx, handlerp, closurep);
+        site->clearTrap(cx->runtime->defaultFreeOp(), handlerp, closurep);
     } else {
         if (handlerp)
             *handlerp = NULL;
         if (closurep)
             *closurep = JSVAL_VOID;
     }
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
 {
-    script->clearTraps(cx);
+    script->clearTraps(cx->runtime->defaultFreeOp());
 }
 
 JS_PUBLIC_API(void)
 JS_ClearAllTrapsForCompartment(JSContext *cx)
 {
     cx->compartment->clearTraps(cx);
 }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2760,25 +2760,25 @@ GCHelperThread::replenishAndFreeLater(vo
     } while (false);
     Foreground::free_(ptr);
 }
 
 /* Must be called with the GC lock taken. */
 void
 GCHelperThread::doSweep()
 {
-    if (JSContext *cx = finalizationContext) {
+    if (finalizationContext) {
         finalizationContext = NULL;
         AutoUnlockGC unlock(rt);
 
         /*
          * We must finalize in the insert order, see comments in
          * finalizeObjects.
          */
-        FreeOp fop(rt, false, true, cx);
+        FreeOp fop(rt, false, true);
         for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
             ArenaLists::backgroundFinalize(&fop, *i);
         finalizeVector.resize(0);
 
         if (freeCursor) {
             void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
             freeElementsAndArray(array, freeCursor);
             freeCursor = freeCursorEnd = NULL;
@@ -2828,34 +2828,34 @@ ReleaseObservedTypes(JSRuntime *rt)
 
     return releaseTypes;
 }
 
 static void
 SweepCompartments(FreeOp *fop, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = fop->runtime();
-    JSCompartmentCallback callback = rt->compartmentCallback;
+    JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
 
     /* Skip the atomsCompartment. */
     JSCompartment **read = rt->compartments.begin() + 1;
     JSCompartment **end = rt->compartments.end();
     JSCompartment **write = read;
     JS_ASSERT(rt->compartments.length() >= 1);
     JS_ASSERT(*rt->compartments.begin() == rt->atomsCompartment);
 
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold &&
             (compartment->arenas.arenaListsAreEmpty() || !rt->hasContexts()))
         {
             compartment->arenas.checkEmptyFreeLists();
             if (callback)
-                JS_ALWAYS_TRUE(callback(fop->context, compartment, JSCOMPARTMENT_DESTROY));
+                callback(fop, compartment);
             if (compartment->principals)
                 JS_DropPrincipals(rt, compartment->principals);
             fop->delete_(compartment);
             continue;
         }
         *write++ = compartment;
     }
     rt->compartments.resize(write - rt->compartments.begin());
@@ -3110,17 +3110,17 @@ SweepPhase(JSContext *cx, JSGCInvocation
     if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep())
         cx->gcBackgroundFree = &rt->gcHelperThread;
 #endif
 
     /* Purge the ArenaLists before sweeping. */
     for (GCCompartmentsIter c(rt); !c.done(); c.next())
         c->arenas.purge();
 
-    FreeOp fop(rt, !!cx->gcBackgroundFree, false, cx);
+    FreeOp fop(rt, !!cx->gcBackgroundFree, false);
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_START);
         if (rt->gcFinalizeCallback)
             rt->gcFinalizeCallback(&fop, JSFINALIZE_START);
     }
 
     /* Finalize unreachable (key,value) pairs in all weak maps. */
     WeakMapBase::sweepAll(&rt->gcMarker);
@@ -3411,17 +3411,17 @@ IncrementalGCSlice(JSContext *cx, int64_
     }
 
     if (rt->gcIncrementalState == MARK_ROOTS) {
         rt->gcMarker.start(rt);
         JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
 
         for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
-            c->discardJitCode(cx);
+            c->discardJitCode(rt->defaultFreeOp());
         }
 
         BeginMarkPhase(rt);
 
         rt->gcIncrementalState = MARK;
     }
 
     if (rt->gcIncrementalState == MARK) {
@@ -4170,17 +4170,17 @@ StartVerifyBarriers(JSContext *cx)
 
     AutoCopyFreeListToArenas copy(rt);
     RecordNativeStackTopForGC(rt);
 
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         r.front()->bitmap.clear();
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
-        c->discardJitCode(cx);
+        c->discardJitCode(rt->defaultFreeOp());
 
     VerifyTracer *trc = new (js_malloc(sizeof(VerifyTracer))) VerifyTracer;
 
     rt->gcNumber++;
     trc->number = rt->gcNumber;
     trc->count = 0;
 
     JS_TracerInit(trc, rt, AccumulateEdge);
@@ -4324,17 +4324,17 @@ EndVerifyBarriers(JSContext *cx)
     JS_ASSERT(trc->number == rt->gcNumber);
     rt->gcNumber++;
 
     /* We need to disable barriers before tracing, which may invoke barriers. */
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->needsBarrier_ = false;
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
-        c->discardJitCode(cx);
+        c->discardJitCode(rt->defaultFreeOp());
 
     rt->gcVerifyData = NULL;
     rt->gcIncrementalState = NO_INCREMENTAL;
 
     JS_TracerInit(trc, rt, MarkFromAutorooter);
 
     AutoGCRooter::traceAll(trc);
 
@@ -4418,17 +4418,17 @@ MaybeVerifyBarriers(JSContext *cx, bool 
 
 static void ReleaseAllJITCode(JSContext *cx)
 {
 #ifdef JS_METHODJIT
     for (GCCompartmentsIter c(cx->runtime); !c.done(); c.next()) {
         mjit::ClearAllFrames(c);
         for (CellIter i(c, FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
-            mjit::ReleaseScriptCode(cx, script);
+            mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
         }
     }
 #endif
 }
 
 /*
  * There are three possible PCCount profiling states:
  *
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2077,91 +2077,97 @@ TypeCompartment::growPendingArray(JSCont
 
     pendingArray = newArray;
     pendingCapacity = newCapacity;
 
     return true;
 }
 
 void
-TypeCompartment::processPendingRecompiles(JSContext *cx)
+TypeCompartment::processPendingRecompiles(FreeOp *fop)
 {
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<RecompileInfo> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
 
     JS_ASSERT(!pending->empty());
 
 #ifdef JS_METHODJIT
 
-    mjit::ExpandInlineFrames(cx->compartment);
+    mjit::ExpandInlineFrames(compartment());
 
     for (unsigned i = 0; i < pending->length(); i++) {
         const RecompileInfo &info = (*pending)[i];
         mjit::JITScript *jit = info.script->getJIT(info.constructing);
         if (jit && jit->chunkDescriptor(info.chunkIndex).chunk)
-            mjit::Recompiler::clearStackReferencesAndChunk(cx, info.script, jit, info.chunkIndex);
+            mjit::Recompiler::clearStackReferencesAndChunk(fop, info.script, jit, info.chunkIndex);
     }
 
 #endif /* JS_METHODJIT */
 
-    cx->delete_(pending);
+    fop->delete_(pending);
 }
 
 void
 TypeCompartment::setPendingNukeTypes(JSContext *cx)
 {
     JS_ASSERT(compartment()->activeInference);
     if (!pendingNukeTypes) {
         if (cx->compartment)
             js_ReportOutOfMemory(cx);
         pendingNukeTypes = true;
     }
 }
 
 void
-TypeCompartment::nukeTypes(JSContext *cx)
-{
-    JS_ASSERT(this == &cx->compartment->types);
-
+TypeCompartment::setPendingNukeTypesNoReport()
+{
+    JS_ASSERT(compartment()->activeInference);
+    if (!pendingNukeTypes)
+        pendingNukeTypes = true;
+}
+
+void
+TypeCompartment::nukeTypes(FreeOp *fop)
+{
     /*
      * This is the usual response if we encounter an OOM while adding a type
      * or resolving type constraints. Reset the compartment to not use type
      * inference, and recompile all scripts.
      *
      * 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) {
-        cx->free_(pendingRecompiles);
+        fop->free_(pendingRecompiles);
         pendingRecompiles = NULL;
     }
 
     inferenceEnabled = false;
 
     /* Update the cached inferenceEnabled bit in all contexts. */
-    for (ContextIter acx(cx->runtime); !acx.done(); acx.next())
+    for (ContextIter acx(fop->runtime()); !acx.done(); acx.next())
         acx->setCompartment(acx->compartment);
 
 #ifdef JS_METHODJIT
 
-    JSCompartment *compartment = cx->compartment;
+    JSCompartment *compartment = this->compartment();
     mjit::ExpandInlineFrames(compartment);
     mjit::ClearAllFrames(compartment);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
 
-    for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+    for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->hasJITCode())
-            mjit::ReleaseScriptCode(cx, script);
+            mjit::ReleaseScriptCode(fop, script);
     }
 #endif /* JS_METHODJIT */
 
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
@@ -5725,17 +5731,17 @@ JSCompartment::getLazyType(JSContext *cx
     return type;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 void
-TypeSet::sweep(JSContext *cx, JSCompartment *compartment)
+TypeSet::sweep(JSCompartment *compartment)
 {
     /*
      * Purge references to type objects that are no longer live. Type sets hold
      * only weak references. For type sets containing more than one object,
      * live entries in the object hash need to be copied to the compartment's
      * new arena.
      */
     unsigned objectCount = baseObjectCount();
@@ -5749,17 +5755,17 @@ TypeSet::sweep(JSContext *cx, JSCompartm
             TypeObjectKey *object = oldArray[i];
             if (object && !IsAboutToBeFinalized(object)) {
                 TypeObjectKey **pentry =
                     HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
                         (compartment, objectSet, objectCount, object);
                 if (pentry)
                     *pentry = object;
                 else
-                    compartment->types.setPendingNukeTypes(cx);
+                    compartment->types.setPendingNukeTypesNoReport();
             }
         }
         setBaseObjectCount(objectCount);
     } else if (objectCount == 1) {
         TypeObjectKey *object = (TypeObjectKey *) objectSet;
         if (IsAboutToBeFinalized(object)) {
             objectSet = NULL;
             setBaseObjectCount(0);
@@ -5784,17 +5790,17 @@ TypeObject::clearProperties()
 /*
  * Before sweeping the arenas themselves, scan all type objects in a
  * 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(JSContext *cx)
+TypeObject::sweep(FreeOp *fop)
 {
     /*
      * We may be regenerating existing type sets containing this object,
      * so reset contributions on each GC to avoid tripping the limit.
      */
     contribution = 0;
 
     if (singleton) {
@@ -5806,17 +5812,17 @@ TypeObject::sweep(JSContext *cx)
          */
         clearProperties();
 
         return;
     }
 
     if (!isMarked()) {
         if (newScript)
-            Foreground::free_(newScript);
+            fop->free_(newScript);
         return;
     }
 
     JSCompartment *compartment = this->compartment();
 
     /*
      * Properties were allocated from the old arena, and need to be copied over
      * to the new one. Don't hang onto properties without the OWN_PROPERTY
@@ -5835,35 +5841,35 @@ TypeObject::sweep(JSContext *cx)
             if (prop && prop->types.isOwnProperty(false)) {
                 Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
                 if (newProp) {
                     Property **pentry =
                         HashSetInsert<jsid,Property,Property>
                             (compartment, propertySet, propertyCount, prop->id);
                     if (pentry) {
                         *pentry = newProp;
-                        newProp->types.sweep(cx, compartment);
+                        newProp->types.sweep(compartment);
                     } else {
-                        compartment->types.setPendingNukeTypes(cx);
+                        compartment->types.setPendingNukeTypesNoReport();
                     }
                 } else {
-                    compartment->types.setPendingNukeTypes(cx);
+                    compartment->types.setPendingNukeTypesNoReport();
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
         if (prop->types.isOwnProperty(false)) {
             Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
             if (newProp) {
                 propertySet = (Property **) newProp;
-                newProp->types.sweep(cx, compartment);
+                newProp->types.sweep(compartment);
             } else {
-                compartment->types.setPendingNukeTypes(cx);
+                compartment->types.setPendingNukeTypesNoReport();
             }
         } else {
             propertySet = NULL;
             setBasePropertyCount(0);
         }
     }
 
     if (basePropertyCount() <= SET_ARRAY_SIZE) {
@@ -5877,37 +5883,37 @@ TypeObject::sweep(JSContext *cx)
      * the next time we compile code which depends on this info.
      */
     if (newScript)
         flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
 }
 
 struct SweepTypeObjectOp
 {
-    JSContext *cx;
-    SweepTypeObjectOp(JSContext *cx) : cx(cx) {}
+    FreeOp *fop;
+    SweepTypeObjectOp(FreeOp *fop) : fop(fop) {}
     void operator()(gc::Cell *cell) {
         TypeObject *object = static_cast<TypeObject *>(cell);
-        object->sweep(cx);
+        object->sweep(fop);
     }
 };
 
 void
-SweepTypeObjects(JSContext *cx, JSCompartment *compartment)
-{
-    SweepTypeObjectOp op(cx);
+SweepTypeObjects(FreeOp *fop, JSCompartment *compartment)
+{
+    SweepTypeObjectOp op(fop);
     gc::ForEachArenaAndCell(compartment, gc::FINALIZE_TYPE_OBJECT, gc::EmptyArenaOp, op);
 }
 
 void
-TypeCompartment::sweep(JSContext *cx)
+TypeCompartment::sweep(FreeOp *fop)
 {
     JSCompartment *compartment = this->compartment();
 
-    SweepTypeObjects(cx, compartment);
+    SweepTypeObjects(fop, compartment);
 
     /*
      * Iterate through the array/object type tables and remove all entries
      * referencing collected data. These tables only hold weak references.
      */
 
     if (arrayTypeTable) {
         for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
@@ -5965,17 +5971,17 @@ TypeCompartment::sweep(JSContext *cx)
         }
     }
 
     /*
      * 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)
-        cx->free_(pendingArray);
+        fop->free_(pendingArray);
 
     pendingArray = NULL;
     pendingCapacity = 0;
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectSet &table)
 {
@@ -5999,37 +6005,37 @@ TypeCompartment::~TypeCompartment()
     if (objectTypeTable)
         Foreground::delete_(objectTypeTable);
 
     if (allocationSiteTable)
         Foreground::delete_(allocationSiteTable);
 }
 
 /* static */ void
-TypeScript::Sweep(JSContext *cx, JSScript *script)
+TypeScript::Sweep(FreeOp *fop, JSScript *script)
 {
     JSCompartment *compartment = script->compartment();
     JS_ASSERT(compartment->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     TypeSet *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(cx, compartment);
+        typeArray[i].sweep(compartment);
 
     TypeResult **presult = &script->types->dynamicList;
     while (*presult) {
         TypeResult *result = *presult;
         Type type = result->type;
 
         if (!type.isUnknown() && !type.isAnyObject() && type.isObject() &&
             IsAboutToBeFinalized(type.objectKey())) {
             *presult = result->next;
-            cx->delete_(result);
+            fop->delete_(result);
         } else {
             presult = &result->next;
         }
     }
 
     /*
      * If the script has nesting state with a most recent activation, we do not
      * need either to mark the call object or clear it if not live. Even with
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -361,17 +361,17 @@ class TypeSet
     TypeConstraint *constraintList;
 
     TypeSet()
         : flags(0), objectSet(NULL), constraintList(NULL)
     {}
 
     void print(JSContext *cx);
 
-    inline void sweep(JSContext *cx, JSCompartment *compartment);
+    inline void sweep(JSCompartment *compartment);
     inline size_t computedSizeOfExcludingThis();
 
     /* Whether this set contains a specific type. */
     inline bool hasType(Type type);
 
     TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
     bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
     bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
@@ -859,17 +859,17 @@ struct TypeObject : gc::Cell
     void setFlags(JSContext *cx, TypeObjectFlags flags);
     void markUnknown(JSContext *cx);
     void clearNewScript(JSContext *cx);
     void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false);
 
     void print(JSContext *cx);
 
     inline void clearProperties();
-    inline void sweep(JSContext *cx);
+    inline void sweep(FreeOp *fop);
 
     inline size_t computedSizeOfExcludingThis();
 
     void sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf);
 
     /*
      * Type objects don't have explicit finalizers. Memory owned by a type
      * object pending deletion is released when weak references are sweeped
@@ -1116,17 +1116,17 @@ class TypeScript
     /* Add a type for a variable in a script. */
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
     static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
 
-    static void Sweep(JSContext *cx, JSScript *script);
+    static void Sweep(FreeOp *fop, JSScript *script);
     inline void trace(JSTracer *trc);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
@@ -1239,34 +1239,35 @@ struct TypeCompartment
      * js_ObjectClass).
      */
     TypeObject *newTypeObject(JSContext *cx, JSScript *script,
                               JSProtoKey kind, JSObject *proto, bool unknown = false);
 
     /* Make an object for an allocation site. */
     TypeObject *newAllocationSiteTypeObject(JSContext *cx, const AllocationSiteKey &key);
 
-    void nukeTypes(JSContext *cx);
-    void processPendingRecompiles(JSContext *cx);
+    void nukeTypes(FreeOp *fop);
+    void processPendingRecompiles(FreeOp *fop);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(JSContext *cx);
+    void setPendingNukeTypesNoReport();
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
     void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);
 
     /* Monitor future effects on a bytecode. */
     void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                          bool returnOnly = false);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
-    void sweep(JSContext *cx);
+    void sweep(FreeOp *fop);
     void finalizeObjects();
 };
 
 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
@@ -197,48 +197,61 @@ TypeIdString(jsid id)
  * information is not collected and does not change.
  *
  * Pins inference results so that intermediate type information, TypeObjects
  * and JSScripts won't be collected during GC. Does additional sanity checking
  * that inference is not reentrant and that recompilations occur properly.
  */
 struct AutoEnterTypeInference
 {
-    JSContext *cx;
+    FreeOp *freeOp;
+    JSCompartment *compartment;
     bool oldActiveAnalysis;
     bool oldActiveInference;
 
     AutoEnterTypeInference(JSContext *cx, bool compiling = false)
-        : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
-          oldActiveInference(cx->compartment->activeInference)
     {
         JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
-        cx->compartment->activeAnalysis = true;
-        cx->compartment->activeInference = true;
+        init(cx->runtime->defaultFreeOp(), cx->compartment);
+    }
+
+    AutoEnterTypeInference(FreeOp *fop, JSCompartment *comp)
+    {
+        init(fop, comp);
     }
 
     ~AutoEnterTypeInference()
     {
-        cx->compartment->activeAnalysis = oldActiveAnalysis;
-        cx->compartment->activeInference = oldActiveInference;
+        compartment->activeAnalysis = oldActiveAnalysis;
+        compartment->activeInference = oldActiveInference;
 
         /*
          * 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 (!cx->compartment->activeInference) {
-            TypeCompartment *types = &cx->compartment->types;
+        if (!compartment->activeInference) {
+            TypeCompartment *types = &compartment->types;
             if (types->pendingNukeTypes)
-                types->nukeTypes(cx);
+                types->nukeTypes(freeOp);
             else if (types->pendingRecompiles)
-                types->processPendingRecompiles(cx);
+                types->processPendingRecompiles(freeOp);
         }
     }
+
+  private:
+    void init(FreeOp *fop, JSCompartment *comp) {
+        freeOp = fop;
+        compartment = comp;
+        oldActiveAnalysis = compartment->activeAnalysis;
+        oldActiveInference = compartment->activeInference;
+        compartment->activeAnalysis = true;
+        compartment->activeInference = true;
+    }
 };
 
 /*
  * Structure marking the currently compiled script, for constraints which can
  * trigger recompilation.
  */
 struct AutoEnterCompilation
 {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -853,17 +853,17 @@ class EvalScriptGuard
       : cx_(cx),
         str_(str),
         script_(NULL) {
         bucket_ = EvalCacheHash(cx, str);
     }
 
     ~EvalScriptGuard() {
         if (script_) {
-            js_CallDestroyScriptHook(cx_, script_);
+            CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
             script_->isActiveEval = false;
             script_->isCachedEval = true;
             script_->evalHashLink() = *bucket_;
             *bucket_ = script_;
         }
     }
 
     void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1617,11 +1617,14 @@ extern JSPrincipals *
 PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx);
 
 extern JSObject *
 NonNullObject(JSContext *cx, const Value &v);
 
 extern const char *
 InformalValueTypeName(const Value &v);
 
+inline void
+DestroyIdArray(FreeOp *fop, JSIdArray *ida);
+
 }  /* namespace js */
 
 #endif /* jsobj_h___ */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1749,9 +1749,15 @@ js_PurgeScopeChainHelper(JSContext *cx, 
 inline bool
 js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
 {
     if (obj->isDelegate())
         return js_PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
+inline void
+js::DestroyIdArray(FreeOp *fop, JSIdArray *ida)
+{
+    fop->free_(ida);
+}
+
 #endif /* jsobjinlines_h___ */
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -226,20 +226,20 @@ Probes::registerMJITCode(JSContext *cx, 
     for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
         (*p)->registerMJITCode(cx, jscr, outerFrame,
                                inlineFrames,
                                mainCodeAddress, mainCodeSize,
                                stubCodeAddress, stubCodeSize);
 }
 
 void
-Probes::discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address)
+Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, JSScript *script, void* address)
 {
     for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
-        (*p)->discardMJITCode(cx, jscr, script, address);
+        (*p)->discardMJITCode(fop, jscr, script, address);
 }
 
 void
 Probes::registerICCode(JSContext *cx,
                        mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
                        void *start, size_t size)
 {
     for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -259,17 +259,17 @@ public:
                                      mjit::JSActiveFrame **inlineFrames);
 
     virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
                                   mjit::JSActiveFrame *outerFrame,
                                   mjit::JSActiveFrame **inlineFrames,
                                   void *mainCodeAddress, size_t mainCodeSize,
                                   void *stubCodeAddress, size_t stubCodeSize) = 0;
 
-    virtual void discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script,
+    virtual void discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, JSScript *script,
                                  void* address) = 0;
 
     virtual void registerICCode(JSContext *cx,
                                 js::mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
                                 void *start, size_t size) = 0;
 #endif
 
     virtual void discardExecutableRegion(void *start, size_t size) = 0;
@@ -311,17 +311,17 @@ registerMJITCode(JSContext *cx, js::mjit
                  mjit::JSActiveFrame **inlineFrames,
                  void *mainCodeAddress, size_t mainCodeSize,
                  void *stubCodeAddress, size_t stubCodeSize);
 
 /*
  * Method JIT code is about to be discarded
  */
 void
-discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address);
+discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, JSScript *script, void* address);
 
 /*
  * IC code has been allocated within the given JITScript
  */
 void
 registerICCode(JSContext *cx,
                mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
                void *start, size_t size);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -383,17 +383,17 @@ typedef void
                     const char *filename,  /* URL of script */
                     unsigned      lineno,     /* first line */
                     JSScript   *script,
                     JSFunction *fun,
                     void       *callerdata);
 
 /* called just before script destruction */
 typedef void
-(* JSDestroyScriptHook)(JSContext *cx,
+(* JSDestroyScriptHook)(JSFreeOp *fop,
                         JSScript  *script,
                         void      *callerdata);
 
 typedef void
 (* JSSourceHandler)(const char *filename, unsigned lineno, const jschar *str,
                     size_t length, void **listenerTSData, void *closure);
 
 /*
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -813,20 +813,20 @@ JSScript::initScriptCounts(JSContext *cx
     InterpreterFrames *frames;
     for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
 
     return true;
 }
 
 void
-JSScript::destroyScriptCounts(JSContext *cx)
+JSScript::destroyScriptCounts(FreeOp *fop)
 {
     if (scriptCounts) {
-        cx->free_(scriptCounts.pcCountsVector);
+        fop->free_(scriptCounts.pcCountsVector);
         scriptCounts.pcCountsVector = NULL;
     }
 }
 
 /*
  * Shared script filename management.
  */
 
@@ -1375,57 +1375,57 @@ js_CallNewScriptHook(JSContext *cx, JSSc
         AutoKeepAtoms keep(cx->runtime);
         hook(cx, script->filename, script->lineno, script, fun,
              cx->runtime->debugHooks.newScriptHookData);
     }
     script->callDestroyHook = true;
 }
 
 void
-js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
+js::CallDestroyScriptHook(FreeOp *fop, JSScript *script)
 {
     if (!script->callDestroyHook)
         return;
 
-    if (JSDestroyScriptHook hook = cx->runtime->debugHooks.destroyScriptHook)
-        hook(cx, script, cx->runtime->debugHooks.destroyScriptHookData);
+    if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook)
+        hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData);
     script->callDestroyHook = false;
-    JS_ClearScriptTraps(cx, script);
+    script->clearTraps(fop);
 }
 
 void
 JSScript::finalize(FreeOp *fop)
 {
-    js_CallDestroyScriptHook(fop->context, this);
+    CallDestroyScriptHook(fop, this);
 
     JS_ASSERT_IF(principals, originPrincipals);
     if (principals)
         JS_DropPrincipals(fop->runtime(), principals);
     if (originPrincipals)
         JS_DropPrincipals(fop->runtime(), originPrincipals);
 
     if (types)
         types->destroy();
 
 #ifdef JS_METHODJIT
-    mjit::ReleaseScriptCode(fop->context, this);
+    mjit::ReleaseScriptCode(fop, this);
 #endif
 
-    destroyScriptCounts(fop->context);
+    destroyScriptCounts(fop);
 
     if (sourceMap)
         fop->free_(sourceMap);
 
     if (debug) {
         jsbytecode *end = code + length;
         for (jsbytecode *pc = code; pc < end; pc++) {
             if (BreakpointSite *site = getBreakpointSite(pc)) {
                 /* Breakpoints are swept before finalization. */
                 JS_ASSERT(site->firstBreakpoint() == NULL);
-                site->clearTrap(fop->context, NULL, NULL);
+                site->clearTrap(fop, NULL, NULL);
                 JS_ASSERT(getBreakpointSite(pc) == NULL);
             }
         }
         fop->free_(debug);
     }
 
     JS_POISON(data, 0xdb, computedSizeOfData());
     fop->free_(data);
@@ -1680,42 +1680,38 @@ JSScript::ensureHasDebug(JSContext *cx)
      */
     InterpreterFrames *frames;
     for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
 
     return true;
 }
 
-bool
-JSScript::recompileForStepMode(JSContext *cx)
+void
+JSScript::recompileForStepMode(FreeOp *fop)
 {
 #ifdef JS_METHODJIT
     if (jitNormal || jitCtor) {
-        mjit::Recompiler::clearStackReferences(cx, this);
-        mjit::ReleaseScriptCode(cx, this);
+        mjit::Recompiler::clearStackReferences(fop, this);
+        mjit::ReleaseScriptCode(fop, this);
     }
 #endif
-    return true;
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
 {
     JS_ASSERT(debug);
 
     uint32_t prior = debug->stepMode;
     debug->stepMode = newValue;
 
     if (!prior != !newValue) {
         /* Step mode has been enabled or disabled. Alert the methodjit. */
-        if (!recompileForStepMode(cx)) {
-            debug->stepMode = prior;
-            return false;
-        }
+        recompileForStepMode(cx->runtime->defaultFreeOp());
 
         if (!stepModeEnabled() && !debug->numSites) {
             cx->free_(debug);
             debug = NULL;
         }
     }
 
     return true;
@@ -1770,28 +1766,28 @@ JSScript::getOrCreateBreakpointSite(JSCo
         JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal);
     else
         site->scriptGlobal = scriptGlobal;
 
     return site;
 }
 
 void
-JSScript::destroyBreakpointSite(JSRuntime *rt, jsbytecode *pc)
+JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc)
 {
     JS_ASSERT(unsigned(pc - code) < length);
 
     BreakpointSite *&site = debug->breakpoints[pc - code];
     JS_ASSERT(site);
 
-    rt->delete_(site);
+    fop->delete_(site);
     site = NULL;
 
     if (--debug->numSites == 0 && !stepModeEnabled()) {
-        rt->free_(debug);
+        fop->free_(debug);
         debug = NULL;
     }
 }
 
 void
 JSScript::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler)
 {
     if (!hasAnyBreakpointsOrStepMode())
@@ -1800,33 +1796,33 @@ JSScript::clearBreakpointsIn(JSContext *
     jsbytecode *end = code + length;
     for (jsbytecode *pc = code; pc < end; pc++) {
         BreakpointSite *site = getBreakpointSite(pc);
         if (site) {
             Breakpoint *nextbp;
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
                 if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
-                    bp->destroy(cx);
+                    bp->destroy(cx->runtime->defaultFreeOp());
             }
         }
     }
 }
 
 void
-JSScript::clearTraps(JSContext *cx)
+JSScript::clearTraps(FreeOp *fop)
 {
     if (!hasAnyBreakpointsOrStepMode())
         return;
 
     jsbytecode *end = code + length;
     for (jsbytecode *pc = code; pc < end; pc++) {
         BreakpointSite *site = getBreakpointSite(pc);
         if (site)
-            site->clearTrap(cx);
+            site->clearTrap(fop);
     }
 }
 
 void
 JSScript::markChildren(JSTracer *trc)
 {
     JS_ASSERT_IF(trc->runtime->gcStrictCompartmentChecking, compartment()->isCollecting());
 
@@ -1912,18 +1908,18 @@ JSScript::applySpeculationFailed(JSConte
                 needsArgsObj_ = false;
                 return false;
             }
         }
     }
 
 #ifdef JS_METHODJIT
     if (hasJITCode()) {
-        mjit::Recompiler::clearStackReferences(cx, this);
-        mjit::ReleaseScriptCode(cx, this);
+        mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), this);
+        mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), this);
     }
 #endif
 
     if (hasAnalysis() && analysis()->ranInference()) {
         types::AutoEnterTypeInference enter(cx);
         for (unsigned off = 0; off < length; off += GetBytecodeLength(code + off)) {
             if (code[off] == JSOP_ARGUMENTS) {
                 types::TypeSet *set = analysis()->pushedTypes(off, 0);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -630,17 +630,17 @@ struct JSScript : public js::gc::Cell
 #endif
 
     js::PCCounts getPCCounts(jsbytecode *pc) {
         JS_ASSERT(size_t(pc - code) < length);
         return scriptCounts.pcCountsVector[pc - code];
     }
 
     bool initScriptCounts(JSContext *cx);
-    void destroyScriptCounts(JSContext *cx);
+    void destroyScriptCounts(js::FreeOp *fop);
 
     jsbytecode *main() {
         return code + mainOffset;
     }
 
     /*
      * computedSizeOfData() is the in-use size of all the data sections.
      * sizeOfData() is the size of the block allocated to hold all the data sections
@@ -746,20 +746,20 @@ struct JSScript : public js::gc::Cell
     uint32_t getClosedVar(uint32_t index) {
         js::ClosedSlotArray *arr = closedVars();
         JS_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
   private:
     /*
-     * Attempt to recompile with or without single-stepping support, as directed
+     * Recompile with or without single-stepping support, as directed
      * by stepModeEnabled().
      */
-    bool recompileForStepMode(JSContext *cx);
+    void recompileForStepMode(js::FreeOp *fop);
 
     /* Attempt to change this->stepMode to |newValue|. */
     bool tryNewStepMode(JSContext *cx, uint32_t newValue);
 
     bool ensureHasDebug(JSContext *cx);
 
   public:
     bool hasBreakpointsAt(jsbytecode *pc) { return !!getBreakpointSite(pc); }
@@ -769,20 +769,20 @@ struct JSScript : public js::gc::Cell
     {
         JS_ASSERT(size_t(pc - code) < length);
         return debug ? debug->breakpoints[pc - code] : NULL;
     }
 
     js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
                                                   js::GlobalObject *scriptGlobal);
 
-    void destroyBreakpointSite(JSRuntime *rt, jsbytecode *pc);
+    void destroyBreakpointSite(js::FreeOp *fop, jsbytecode *pc);
 
     void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler);
-    void clearTraps(JSContext *cx);
+    void clearTraps(js::FreeOp *fop);
 
     void markTrapClosures(JSTracer *trc);
 
     /*
      * Set or clear the single-step flag. If the flag is set or the count
      * (adjusted by changeStepModeCount) is non-zero, then the script is in
      * single-step mode. (JSD uses an on/off-style interface; Debugger uses a
      * count-style interface.)
@@ -831,20 +831,20 @@ StackDepth(JSScript *script)
  * New-script-hook calling is factored from NewScriptFromEmitter so that it
  * and callers of XDRScript can share this code.  In the case of callers
  * of XDRScript, the hook should be invoked only after successful decode
  * of any owning function (the fun parameter) or script object (null fun).
  */
 extern JS_FRIEND_API(void)
 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
 
+namespace js {
+
 extern void
-js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
-
-namespace js {
+CallDestroyScriptHook(FreeOp *fop, JSScript *script);
 
 extern const char *
 SaveScriptFilename(JSContext *cx, const char *filename);
 
 extern void
 MarkScriptFilename(const char *filename);
 
 extern void
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -921,25 +921,25 @@ JSXMLArray<T>::trim()
     if (capacity & JSXML_PRESET_CAPACITY)
         return;
     if (length < capacity)
         setCapacity(NULL, length);
 }
 
 template<class T>
 void
-JSXMLArray<T>::finish(JSContext *cx)
-{
-    if (!cx->runtime->gcRunning) {
+JSXMLArray<T>::finish(FreeOp *fop)
+{
+    if (!fop->runtime()->gcRunning) {
         /* We need to clear these to trigger a write barrier. */
         for (uint32_t i = 0; i < length; i++)
             vector[i].~HeapPtr<T>();
     }
 
-    cx->free_(vector);
+    fop->free_(vector);
 
     while (JSXMLArrayCursor<T> *cursor = cursors)
         cursor->disconnect();
 
 #ifdef DEBUG
     memset(this, 0xd5, sizeof *this);
 #endif
 }
@@ -1173,20 +1173,20 @@ HAS_NS_AFTER_XML(const jschar *chars)
 
 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
 
 void
 JSXML::finalize(FreeOp *fop)
 {
     if (JSXML_HAS_KIDS(this)) {
-        xml_kids.finish(fop->context);
+        xml_kids.finish(fop);
         if (xml_class == JSXML_CLASS_ELEMENT) {
-            xml_namespaces.finish(fop->context);
-            xml_attrs.finish(fop->context);
+            xml_namespaces.finish(fop);
+            xml_attrs.finish(fop);
         }
     }
 #ifdef DEBUG_notme
     JS_REMOVE_LINK(&links);
 #endif
 }
 
 static JSObject *
@@ -4480,17 +4480,17 @@ PutProperty(JSContext *cx, JSObject *obj
         }
 
         /* 14. */
         if (primitiveAssign) {
             JSXMLArrayCursor<JSXML> cursor(&xml->xml_kids);
             cursor.index = matchIndex;
             kid = cursor.getCurrent();
             if (JSXML_HAS_KIDS(kid)) {
-                kid->xml_kids.finish(cx);
+                kid->xml_kids.finish(cx->runtime->defaultFreeOp());
                 kid->xml_kids.init();
                 ok = kid->xml_kids.setCapacity(cx, 1);
             }
 
             /* 14(b-c). */
             /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */
             if (ok) {
                 ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp);
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -67,17 +67,17 @@ struct JSXMLArray
     JSXMLArrayCursor<T> *cursors;
 
     void init() {
         length = capacity = 0;
         vector = NULL;
         cursors = NULL;
     }
 
-    void finish(JSContext *cx);
+    void finish(js::FreeOp *fop);
 
     bool setCapacity(JSContext *cx, uint32_t capacity);
     void trim();
 };
 
 template<class T>
 struct JSXMLArrayCursor
 {
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1308,45 +1308,45 @@ JITChunk::~JITChunk()
         callICs_[i].releasePools();
         if (callICs_[i].fastGuardedObject)
             callICs_[i].purgeGuardedObject();
     }
 #endif
 }
 
 void
-JITScript::destroy(JSContext *cx)
+JITScript::destroy(FreeOp *fop)
 {
     for (unsigned i = 0; i < nchunks; i++)
-        destroyChunk(cx, i);
+        destroyChunk(fop, i);
 
     if (shimPool)
         shimPool->release();
 }
 
 void
-JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses)
+JITScript::destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses)
 {
     ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
 
     if (desc.chunk) {
-        Probes::discardMJITCode(cx, this, script, desc.chunk->code.m_code.executableAddress());
-        cx->delete_(desc.chunk);
+        Probes::discardMJITCode(fop, this, script, desc.chunk->code.m_code.executableAddress());
+        fop->delete_(desc.chunk);
         desc.chunk = NULL;
 
         CrossChunkEdge *edges = this->edges();
         for (unsigned i = 0; i < nedges; i++) {
             CrossChunkEdge &edge = edges[i];
             if (edge.source >= desc.begin && edge.source < desc.end) {
                 edge.sourceJump1 = edge.sourceJump2 = NULL;
 #ifdef JS_CPU_X64
                 edge.sourceTrampoline = NULL;
 #endif
                 if (edge.jumpTableEntries) {
-                    cx->delete_(edge.jumpTableEntries);
+                    fop->delete_(edge.jumpTableEntries);
                     edge.jumpTableEntries = NULL;
                 }
             } else if (edge.target >= desc.begin && edge.target < desc.end) {
                 edge.targetLabel = NULL;
                 patchEdge(edge, edge.shimLabel);
             }
         }
     }
@@ -1433,28 +1433,28 @@ mjit::JITChunk::computedSizeOfIncludingT
 /* Please keep in sync with Compiler::finishThisUp! */
 size_t
 mjit::JITChunk::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     return mallocSizeOf(this);
 }
 
 void
-mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct)
+mjit::ReleaseScriptCode(FreeOp *fop, JSScript *script, bool construct)
 {
     // NB: The recompiler may call ReleaseScriptCode, in which case it
     // will get called again when the script is destroyed, so we
     // must protect against calling ReleaseScriptCode twice.
 
     JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal;
     void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal;
 
     if (*pjit) {
-        (*pjit)->destroy(cx);
-        cx->free_(*pjit);
+        (*pjit)->destroy(fop);
+        fop->free_(*pjit);
         *pjit = NULL;
         *parity = NULL;
     }
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -861,18 +861,18 @@ struct JITScript
 
     /* Patch any compiled sources in edge to jump to label. */
     void patchEdge(const CrossChunkEdge &edge, void *label);
 
     jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline);
 
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
 
-    void destroy(JSContext *cx);
-    void destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses = true);
+    void destroy(FreeOp *fop);
+    void destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses = true);
 };
 
 /*
  * Execute the given mjit code. This is a low-level call and callers must
  * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
  */
 JaegerStatus EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit,
                             bool partial);
@@ -902,25 +902,25 @@ enum CompileRequest
     CompileRequest_JIT
 };
 
 CompileStatus
 CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
              bool construct, CompileRequest request);
 
 void
-ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct);
+ReleaseScriptCode(FreeOp *fop, JSScript *script, bool construct);
 
 inline void
-ReleaseScriptCode(JSContext *cx, JSScript *script)
+ReleaseScriptCode(FreeOp *fop, JSScript *script)
 {
     if (script->jitCtor)
-        mjit::ReleaseScriptCode(cx, script, true);
+        mjit::ReleaseScriptCode(fop, script, true);
     if (script->jitNormal)
-        mjit::ReleaseScriptCode(cx, script, false);
+        mjit::ReleaseScriptCode(fop, script, false);
 }
 
 // Expand all stack frames inlined by the JIT within a compartment.
 void
 ExpandInlineFrames(JSCompartment *compartment);
 
 // Return all VMFrames in a compartment to the interpreter. This must be
 // followed by destroying all JIT code in the compartment.
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -396,38 +396,39 @@ ClearAllFrames(JSCompartment *compartmen
  * - For VMFrames with a stub call return address in the original script,
  *   redirect to the interpoline.
  *
  * - For VMFrames whose entryncode address (the value of entryfp->ncode before
  *   being clobbered with JaegerTrampolineReturn) is in the original script,
  *   redirect that entryncode to the interpoline.
  */
 void
-Recompiler::clearStackReferences(JSContext *cx, JSScript *script)
+Recompiler::clearStackReferences(FreeOp *fop, JSScript *script)
 {
     JS_ASSERT(script->hasJITCode());
 
     JaegerSpew(JSpew_Recompile, "recompiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
                script->filename, script->lineno, script->length);
 
-    types::AutoEnterTypeInference enter(cx, true);
+    JSCompartment *comp = script->compartment();
+    types::AutoEnterTypeInference enter(fop, comp);
 
     /*
      * The strategy for this goes as follows:
      * 
      * 1) Scan the stack, looking at all return addresses that could go into JIT
      *    code.
      * 2) If an address corresponds to a call site registered by |callSite| during
      *    the last compilation, patch it to go to the interpoline.
      * 3) Purge the old compiled state.
      */
 
     // Find all JIT'd stack frames to account for return addresses that will
     // need to be patched after recompilation.
-    for (VMFrame *f = script->compartment()->jaegerCompartment()->activeFrame();
+    for (VMFrame *f = comp->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
         // Scan all frames owned by this VMFrame.
         StackFrame *end = f->entryfp->prev();
         StackFrame *next = NULL;
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             if (fp->script() != script) {
@@ -445,52 +446,52 @@ Recompiler::clearStackReferences(JSConte
                     JITChunk *chunk = fp->jit()->findCodeChunk(*addr);
                     patchCall(chunk, fp, addr);
                 }
             }
 
             next = fp;
         }
 
-        patchFrame(cx->compartment, f, script);
+        patchFrame(comp, f, script);
     }
 
-    cx->compartment->types.recompilations++;
+    comp->types.recompilations++;
 }
 
 void
-Recompiler::clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
+Recompiler::clearStackReferencesAndChunk(FreeOp *fop, JSScript *script,
                                          JITScript *jit, size_t chunkIndex,
                                          bool resetUses)
 {
-    Recompiler::clearStackReferences(cx, script);
+    Recompiler::clearStackReferences(fop, script);
 
     bool releaseChunk = true;
     if (jit->nchunks > 1) {
         // If we are in the middle of a native call from a native or getter IC,
         // we need to make sure all JIT code for the script is purged, as
         // otherwise we will have orphaned the native stub but pointers to it
         // still exist in the containing chunk.
-        for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
+        for (VMFrame *f = script->compartment()->jaegerCompartment()->activeFrame();
              f != NULL;
              f = f->previous) {
             if (f->fp()->script() == script) {
                 JS_ASSERT(f->stubRejoin != REJOIN_NATIVE &&
                           f->stubRejoin != REJOIN_NATIVE_LOWERED &&
                           f->stubRejoin != REJOIN_NATIVE_GETTER);
                 if (f->stubRejoin == REJOIN_NATIVE_PATCHED) {
-                    mjit::ReleaseScriptCode(cx, script);
+                    mjit::ReleaseScriptCode(fop, script);
                     releaseChunk = false;
                     break;
                 }
             }
         }
     }
 
     if (releaseChunk)
-        jit->destroyChunk(cx, chunkIndex, resetUses);
+        jit->destroyChunk(fop, chunkIndex, resetUses);
 }
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* JS_METHODJIT */
 
--- a/js/src/methodjit/Retcon.h
+++ b/js/src/methodjit/Retcon.h
@@ -63,22 +63,22 @@ namespace mjit {
  * JITed code, fixing up the stack in the process.
  */
 class Recompiler {
 public:
 
     // Clear all uses of compiled code for script on the stack. This must be
     // followed by destroying all JIT code for the script.
     static void
-    clearStackReferences(JSContext *cx, JSScript *script);
+    clearStackReferences(FreeOp *fop, JSScript *script);
 
     // Clear all uses of compiled code for script on the stack, along with
     // the specified compiled chunk.
     static void
-    clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
+    clearStackReferencesAndChunk(FreeOp *fop, JSScript *script,
                                  JITScript *jit, size_t chunkIndex,
                                  bool resetUses = true);
 
     static void
     expandInlineFrames(JSCompartment *compartment, StackFrame *fp, mjit::CallSite *inlined,
                        StackFrame *next, VMFrame *f);
 
     static void patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -813,18 +813,18 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
     ExpandInlineFrames(f.cx->compartment);
-    Recompiler::clearStackReferencesAndChunk(f.cx, f.script(), f.jit(), f.chunkIndex(),
-                                             /* resetUses = */ false);
+    Recompiler::clearStackReferencesAndChunk(f.cx->runtime->defaultFreeOp(), f.script(), f.jit(),
+                                             f.chunkIndex(), /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
     Value rval;
 
     /*
@@ -1665,18 +1665,18 @@ stubs::InvariantFailure(VMFrame &f, void
 
     /* Recompile the outermost script, and don't hoist any bounds checks. */
     JSScript *script = f.fp()->script();
     JS_ASSERT(!script->failedBoundsCheck);
     script->failedBoundsCheck = true;
 
     ExpandInlineFrames(f.cx->compartment);
 
-    mjit::Recompiler::clearStackReferences(f.cx, script);
-    mjit::ReleaseScriptCode(f.cx, script);
+    mjit::Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
+    mjit::ReleaseScriptCode(f.cx->runtime->defaultFreeOp(), script);
 
     /* Return the same value (if any) as the call triggering the invariant failure. */
     return rval;
 }
 
 void JS_FASTCALL
 stubs::Exception(VMFrame &f)
 {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -278,90 +278,77 @@ ScriptGlobal(JSContext *cx, JSScript *sc
     for (AllFramesIter i(cx->stack.space()); ; ++i) {
         JS_ASSERT(!i.done());
         if (i.fp()->maybeScript() == script)
             return &i.fp()->scopeChain().global();
     }
     JS_NOT_REACHED("ScriptGlobal: live non-held script not on stack");
 }
 
-bool
-BreakpointSite::recompile(JSContext *cx, bool forTrap)
+void
+BreakpointSite::recompile(FreeOp *fop)
 {
 #ifdef JS_METHODJIT
     if (script->hasJITCode()) {
-        Maybe<AutoCompartment> ac;
-        if (!forTrap) {
-            ac.construct(cx, ScriptGlobal(cx, script, scriptGlobal));
-            if (!ac.ref().enter())
-                return false;
-        }
-        mjit::Recompiler::clearStackReferences(cx, script);
-        mjit::ReleaseScriptCode(cx, script);
+        mjit::Recompiler::clearStackReferences(fop, script);
+        mjit::ReleaseScriptCode(fop, script);
     }
 #endif
-    return true;
-}
-
-bool
-BreakpointSite::inc(JSContext *cx)
-{
-    if (enabledCount == 0 && !trapHandler) {
-        if (!recompile(cx, false))
-            return false;
-    }
-    enabledCount++;
-    return true;
 }
 
 void
-BreakpointSite::dec(JSContext *cx)
+BreakpointSite::inc(FreeOp *fop)
+{
+    if (enabledCount == 0 && !trapHandler)
+        recompile(fop);
+    enabledCount++;
+}
+
+void
+BreakpointSite::dec(FreeOp *fop)
 {
     JS_ASSERT(enabledCount > 0);
     enabledCount--;
     if (enabledCount == 0 && !trapHandler)
-        recompile(cx, false);  /* ignore failure */
-}
-
-bool
-BreakpointSite::setTrap(JSContext *cx, JSTrapHandler handler, const Value &closure)
-{
-    if (enabledCount == 0) {
-        if (!recompile(cx, true))
-            return false;
-    }
-    trapHandler = handler;
-    trapClosure = closure;
-    return true;
+        recompile(fop);
 }
 
 void
-BreakpointSite::clearTrap(JSContext *cx, JSTrapHandler *handlerp, Value *closurep)
+BreakpointSite::setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure)
+{
+    if (enabledCount == 0)
+        recompile(fop);
+    trapHandler = handler;
+    trapClosure = closure;
+}
+
+void
+BreakpointSite::clearTrap(FreeOp *fop, JSTrapHandler *handlerp, Value *closurep)
 {
     if (handlerp)
         *handlerp = trapHandler;
     if (closurep)
         *closurep = trapClosure;
 
     trapHandler = NULL;
     trapClosure = UndefinedValue();
     if (enabledCount == 0) {
-        if (!cx->runtime->gcRunning) {
+        if (!fop->runtime()->gcRunning) {
             /* If the GC is running then the script is being destroyed. */
-            recompile(cx, true);  /* ignore failure */
+            recompile(fop);
         }
-        destroyIfEmpty(cx->runtime);
+        destroyIfEmpty(fop);
     }
 }
 
 void
-BreakpointSite::destroyIfEmpty(JSRuntime *rt)
+BreakpointSite::destroyIfEmpty(FreeOp *fop)
 {
     if (JS_CLIST_IS_EMPTY(&breakpoints) && !trapHandler)
-        script->destroyBreakpointSite(rt, pc);
+        script->destroyBreakpointSite(fop, pc);
 }
 
 Breakpoint *
 BreakpointSite::firstBreakpoint() const
 {
     if (JS_CLIST_IS_EMPTY(&breakpoints))
         return NULL;
     return Breakpoint::fromSiteLinks(JS_NEXT_LINK(&breakpoints));
@@ -391,25 +378,24 @@ Breakpoint::fromDebuggerLinks(JSCList *l
 
 Breakpoint *
 Breakpoint::fromSiteLinks(JSCList *links)
 {
     return (Breakpoint *) ((unsigned char *) links - offsetof(Breakpoint, siteLinks));
 }
 
 void
-Breakpoint::destroy(JSContext *cx)
+Breakpoint::destroy(FreeOp *fop)
 {
     if (debugger->enabled)
-        site->dec(cx);
+        site->dec(fop);
     JS_REMOVE_LINK(&debuggerLinks);
     JS_REMOVE_LINK(&siteLinks);
-    JSRuntime *rt = cx->runtime;
-    site->destroyIfEmpty(rt);
-    rt->delete_(this);
+    site->destroyIfEmpty(fop);
+    fop->delete_(this);
 }
 
 Breakpoint *
 Breakpoint::nextInDebugger()
 {
     JSCList *link = JS_NEXT_LINK(&debuggerLinks);
     return (link == &debugger->breakpoints) ? NULL : fromDebuggerLinks(link);
 }
@@ -1471,40 +1457,40 @@ Debugger::sweepAll(FreeOp *fop)
 
         if (IsAboutToBeFinalized(dbg->object)) {
             /*
              * dbg is being GC'd. Detach it from its debuggees. The debuggee
              * might be GC'd too. Since detaching requires access to both
              * objects, this must be done before finalize time.
              */
             for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
-                dbg->removeDebuggeeGlobal(fop->context, e.front(), NULL, &e);
+                dbg->removeDebuggeeGlobal(fop, e.front(), NULL, &e);
         }
 
     }
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
         /* For each debuggee being GC'd, detach it from all its debuggers. */
         GlobalObjectSet &debuggees = (*c)->getDebuggees();
         for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject *global = e.front();
             if (IsAboutToBeFinalized(global))
-                detachAllDebuggersFromGlobal(fop->context, global, &e);
+                detachAllDebuggersFromGlobal(fop, global, &e);
         }
     }
 }
 
 void
-Debugger::detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global,
+Debugger::detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
                                        GlobalObjectSet::Enum *compartmentEnum)
 {
     const GlobalObject::DebuggerVector *debuggers = global->getDebuggers();
     JS_ASSERT(!debuggers->empty());
     while (!debuggers->empty())
-        debuggers->back()->removeDebuggeeGlobal(cx, global, compartmentEnum, NULL);
+        debuggers->back()->removeDebuggeeGlobal(fop, global, compartmentEnum, NULL);
 }
 
 void
 Debugger::finalize(FreeOp *fop, JSObject *obj)
 {
     Debugger *dbg = fromJSObject(obj);
     if (!dbg)
         return;
@@ -1570,33 +1556,20 @@ JSBool
 Debugger::setEnabled(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.set enabled", 1);
     THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg);
     bool enabled = js_ValueToBoolean(args[0]);
 
     if (enabled != dbg->enabled) {
         for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-            if (enabled) {
-                if (!bp->site->inc(cx)) {
-                    /*
-                     * Roll back the changes on error to keep the
-                     * BreakpointSite::enabledCount counters correct.
-                     */
-                    for (Breakpoint *bp2 = dbg->firstBreakpoint();
-                         bp2 != bp;
-                         bp2 = bp2->nextInDebugger())
-                    {
-                        bp->site->dec(cx);
-                    }
-                    return false;
-                }
-            } else {
-                bp->site->dec(cx);
-            }
+            if (enabled)
+                bp->site->inc(cx->runtime->defaultFreeOp());
+            else
+                bp->site->dec(cx->runtime->defaultFreeOp());
         }
     }
 
     dbg->enabled = enabled;
     args.rval().setUndefined();
     return true;
 }
 
@@ -1751,17 +1724,17 @@ Debugger::removeDebuggee(JSContext *cx, 
 {
     REQUIRE_ARGC("Debugger.removeDebuggee", 1);
     THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
     if (!referent)
         return false;
     GlobalObject *global = &referent->global();
     if (dbg->debuggees.has(global))
-        dbg->removeDebuggeeGlobal(cx, global, NULL, NULL);
+        dbg->removeDebuggeeGlobal(cx->runtime->defaultFreeOp(), global, NULL, NULL);
     args.rval().setUndefined();
     return true;
 }
 
 JSBool
 Debugger::hasDebuggee(JSContext *cx, unsigned argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.hasDebuggee", 1);
@@ -1941,17 +1914,17 @@ Debugger::addDebuggeeGlobal(JSContext *c
         }
         JS_ASSERT(v->back() == this);
         v->popBack();
     }
     return false;
 }
 
 void
-Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
+Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
                                GlobalObjectSet::Enum *compartmentEnum,
                                GlobalObjectSet::Enum *debugEnum)
 {
     /*
      * Each debuggee is in two HashSets: one for its compartment and one for
      * its debugger (this). The caller might be enumerating either set; if so,
      * use HashSet::Enum::removeFront rather than HashSet::remove below, to
      * avoid invalidating the live enumerator.
@@ -1987,17 +1960,17 @@ Debugger::removeDebuggeeGlobal(JSContext
     JS_ASSERT(p != v->end());
 
     /*
      * The relation must be removed from up to three places: *v and debuggees
      * for sure, and possibly the compartment's debuggee set.
      */
     v->erase(p);
     if (v->empty())
-        global->compartment()->removeDebuggee(cx, global, compartmentEnum);
+        global->compartment()->removeDebuggee(fop, global, compartmentEnum);
     if (debugEnum)
         debugEnum->removeFront();
     else
         debuggees.remove(global);
 }
 
 /* A set of JSCompartment pointers. */
 typedef HashSet<JSCompartment *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy> CompartmentSet;
@@ -2603,24 +2576,23 @@ DebuggerScript_setBreakpoint(JSContext *
     JSObject *handler = NonNullObject(cx, args[1]);
     if (!handler)
         return false;
 
     jsbytecode *pc = script->code + offset;
     BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, scriptGlobal);
     if (!site)
         return false;
-    if (site->inc(cx)) {
-        if (cx->runtime->new_<Breakpoint>(dbg, site, handler)) {
-            args.rval().setUndefined();
-            return true;
-        }
-        site->dec(cx);
+    site->inc(cx->runtime->defaultFreeOp());
+    if (cx->runtime->new_<Breakpoint>(dbg, site, handler)) {
+        args.rval().setUndefined();
+        return true;
     }
-    site->destroyIfEmpty(cx->runtime);
+    site->dec(cx->runtime->defaultFreeOp());
+    site->destroyIfEmpty(cx->runtime->defaultFreeOp());
     return false;
 }
 
 static JSBool
 DebuggerScript_getBreakpoints(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script);
     Debugger *dbg = Debugger::fromChildJSObject(obj);
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -117,17 +117,17 @@ class Debugger {
     ObjectWeakMap objects;
 
     /* The map from debuggee Envs to Debugger.Environment instances. */
     ObjectWeakMap environments;
 
     class FrameRange;
 
     bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj);
-    void removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
+    void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
                               GlobalObjectSet::Enum *compartmentEnum,
                               GlobalObjectSet::Enum *debugEnum);
 
     /*
      * Cope with an error or exception in a debugger hook.
      *
      * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
      * addition, vp is non-null, then parse the value returned by
@@ -259,17 +259,17 @@ class Debugger {
      *
      * Debugger::markAllIteratively handles the last case. If it finds any
      * Debugger objects that are definitely live but not yet marked, it marks
      * them and returns true. If not, it returns false.
      */
     static void markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer);
     static bool markAllIteratively(GCMarker *trc);
     static void sweepAll(FreeOp *fop);
-    static void detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global,
+    static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
                                              GlobalObjectSet::Enum *compartmentEnum);
 
     static inline JSTrapStatus onEnterFrame(JSContext *cx, Value *vp);
     static inline bool onLeaveFrame(JSContext *cx, bool ok);
     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, Value *vp);
     static inline JSTrapStatus onExceptionUnwind(JSContext *cx, Value *vp);
     static inline void onNewScript(JSContext *cx, JSScript *script,
                                    GlobalObject *compileAndGoGlobal);
@@ -394,30 +394,30 @@ class BreakpointSite {
      */
     GlobalObject *scriptGlobal;
 
     JSCList breakpoints;  /* cyclic list of all js::Breakpoints at this instruction */
     size_t enabledCount;  /* number of breakpoints in the list that are enabled */
     JSTrapHandler trapHandler;  /* jsdbgapi trap state */
     HeapValue trapClosure;
 
-    bool recompile(JSContext *cx, bool forTrap);
+    void recompile(FreeOp *fop);
 
   public:
     BreakpointSite(JSScript *script, jsbytecode *pc);
     Breakpoint *firstBreakpoint() const;
     bool hasBreakpoint(Breakpoint *bp);
     bool hasTrap() const { return !!trapHandler; }
     GlobalObject *getScriptGlobal() const { return scriptGlobal; }
 
-    bool inc(JSContext *cx);
-    void dec(JSContext *cx);
-    bool setTrap(JSContext *cx, JSTrapHandler handler, const Value &closure);
-    void clearTrap(JSContext *cx, JSTrapHandler *handlerp = NULL, Value *closurep = NULL);
-    void destroyIfEmpty(JSRuntime *rt);
+    void inc(FreeOp *fop);
+    void dec(FreeOp *fop);
+    void setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure);
+    void clearTrap(FreeOp *fop, JSTrapHandler *handlerp = NULL, Value *closurep = NULL);
+    void destroyIfEmpty(FreeOp *fop);
 };
 
 /*
  * Each Breakpoint is a member of two linked lists: its debugger's list and its
  * site's list.
  *
  * GC rules:
  *   - script is live, breakpoint exists, and debugger is enabled
@@ -444,17 +444,17 @@ class Breakpoint {
     js::HeapPtrObject handler;
     JSCList debuggerLinks;
     JSCList siteLinks;
 
   public:
     static Breakpoint *fromDebuggerLinks(JSCList *links);
     static Breakpoint *fromSiteLinks(JSCList *links);
     Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler);
-    void destroy(JSContext *cx);
+    void destroy(FreeOp *fop);
     Breakpoint *nextInDebugger();
     Breakpoint *nextInSite();
     const HeapPtrObject &getHandler() const { return handler; }
     HeapPtrObject &getHandlerRef() { return handler; }
 };
 
 Debugger *
 Debugger::fromLinks(JSCList *links)
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -365,18 +365,18 @@ GlobalObject::clear(JSContext *cx)
     /*
      * Destroy compiled code for any scripts parented to this global. Call ICs
      * can directly call scripts which have associated JIT code, and do so
      * without checking whether the script's global has been cleared.
      */
     for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->compileAndGo && script->hasJITCode() && script->hasClearedGlobal()) {
-            mjit::Recompiler::clearStackReferences(cx, script);
-            mjit::ReleaseScriptCode(cx, script);
+            mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), script);
+            mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), script);
         }
     }
 #endif
 }
 
 bool
 GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx)
 {
@@ -502,15 +502,15 @@ GlobalObject::addDebugger(JSContext *cx,
         return false;
 #ifdef DEBUG
     for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
         JS_ASSERT(*p != dbg);
 #endif
     if (debuggers->empty() && !compartment()->addDebuggee(cx, this))
         return false;
     if (!debuggers->append(dbg)) {
-        compartment()->removeDebuggee(cx, this);
+        compartment()->removeDebuggee(cx->runtime->defaultFreeOp(), this);
         return false;
     }
     return true;
 }
 
 } // namespace js
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -246,42 +246,38 @@ ContextCallback(JSContext *cx, unsigned 
     return true;
 }
 
 xpc::CompartmentPrivate::~CompartmentPrivate()
 {
     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
 }
 
-static JSBool
-CompartmentCallback(JSContext *cx, JSCompartment *compartment, unsigned op)
+static void
+CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
 {
-    JS_ASSERT(op == JSCOMPARTMENT_DESTROY);
-
     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     if (!self)
-        return true;
+        return;
 
     nsAutoPtr<xpc::CompartmentPrivate>
         priv(static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)));
     if (!priv)
-        return true;
+        return;
 
     JS_SetCompartmentPrivate(compartment, nsnull);
 
     xpc::PtrAndPrincipalHashKey *key = priv->key;
     XPCCompartmentMap &map = self->GetCompartmentMap();
 #ifdef DEBUG
     JSCompartment *current = NULL;
     NS_ASSERTION(map.Get(key, &current), "no compartment?");
     NS_ASSERTION(current == compartment, "compartment mismatch");
 #endif
     map.Remove(key);
-
-    return true;
 }
 
 struct ObjectHolder : public JSDHashEntryHdr
 {
     void *holder;
     nsScriptObjectTracer* tracer;
 };
 
@@ -1996,17 +1992,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
 #ifdef MOZ_ASAN
     // ASan requires more stack space due to redzones
     JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
 #else  
     JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
 #endif
     JS_SetContextCallback(mJSRuntime, ContextCallback);
-    JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
+    JS_SetDestroyCompartmentCallback(mJSRuntime, CompartmentDestroyedCallback);
     JS_SetGCCallback(mJSRuntime, GCCallback);
     JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
     JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
     JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
     JS_SetWrapObjectCallbacks(mJSRuntime,
                               xpc::WrapperFactory::Rewrap,
                               xpc::WrapperFactory::PrepareForWrapping);
     js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);