Bug 737364 - part 2 - replace JSContext with JSRuntime in the GC-related API
authorIgor Bukanov <igor@mir2.org>
Wed, 28 Mar 2012 12:13:30 +0200
changeset 94531 d5589821fc04eb82b238f2648fcb19ef0febcbf4
parent 94530 7dffb77aabcdd53b3e7d7098e36a0255d7986035
child 94532 2dd66b1911c1bf61e1cd646fb2a8a44538aaa4d6
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs737364
milestone14.0a1
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
Bug 737364 - part 2 - replace JSContext with JSRuntime in the GC-related API This part replaces the JSContext *cx argument in most GC-related API with JSRuntime *rt. When possible, the patch removes the code to obtain a temporary cx just to run the GC. The patch also removes JS_DestroyContextMaybeGC. That function is not used in FF code base and its implementation is broken. It requires that the context has an entered compartment when it is destroyed, which in turns implies a missing leave compartment call.
dom/workers/WorkerPrivate.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/jsd/jsd_xpc.cpp
js/jsd/jsdebug.c
js/jsd/jsdebug.h
js/src/builtin/TestingFunctions.cpp
js/src/jsapi-tests/testBug604087.cpp
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi-tests/testExternalStrings.cpp
js/src/jsapi-tests/testGCOutOfMemory.cpp
js/src/jsapi-tests/testIntern.cpp
js/src/jsapi-tests/testScriptObject.cpp
js/src/jsapi-tests/testTrap.cpp
js/src/jsapi-tests/testXDR.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/xpconnect/idl/xpccomponents.idl
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/nsXPConnect.cpp
toolkit/xre/nsXREDirProvider.cpp
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2797,18 +2797,17 @@ WorkerPrivate::OperationCallback(JSConte
     // Run all control events now.
     mayContinue = ProcessAllControlRunnables();
 
     if (!mayContinue || !mSuspended) {
       break;
     }
 
     // Clean up before suspending.
-    JS_FlushCaches(aCx);
-    JS_GC(aCx);
+    JS_GC(JS_GetRuntime(aCx));
 
     while ((mayContinue = MayContinueRunning())) {
       MutexAutoLock lock(mMutex);
       if (!mControlQueue.IsEmpty()) {
         break;
       }
 
       mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
@@ -3886,22 +3885,23 @@ WorkerPrivate::UpdateGCZealInternal(JSCo
 #endif
 
 void
 WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
                                       bool aCollectChildren)
 {
   AssertIsOnWorkerThread();
 
-  js::PrepareForFullGC(JS_GetRuntime(aCx));
+  JSRuntime *rt = JS_GetRuntime(aCx);
+  js::PrepareForFullGC(rt);
   if (aShrinking) {
-    js::ShrinkingGC(aCx, js::gcreason::DOM_WORKER);
+    js::ShrinkingGC(rt, js::gcreason::DOM_WORKER);
   }
   else {
-    js::GCForReason(aCx, js::gcreason::DOM_WORKER);
+    js::GCForReason(rt, js::gcreason::DOM_WORKER);
   }
 
   if (aCollectChildren) {
     for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
       mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
     }
   }
 }
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -404,19 +404,19 @@ DumpXPC(JSContext *cx,
     return JS_TRUE;
 }
 
 static JSBool
 GC(JSContext *cx,
    unsigned argc,
    jsval *vp)
 {
-    JS_GC(cx);
+    JSRuntime *rt = JS_GetRuntime(cx);
+    JS_GC(rt);
 #ifdef JS_GCMETER
-    JSRuntime *rt = JS_GetRuntime(cx);
     js_DumpGCStats(rt, stdout);
 #endif
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
 }
 
 #ifdef JS_GC_ZEAL
 static JSBool
@@ -1049,26 +1049,25 @@ XPCShellEnvironment::~XPCShellEnvironmen
         JS_BeginRequest(mCx);
 
         JSObject* global = GetGlobalObject();
         if (global) {
             JS_ClearScope(mCx, global);
         }
         mGlobalHolder.Release();
 
-        JS_GC(mCx);
+        JSRuntime *rt = JS_GetRuntime(mCx);
+        JS_GC(rt);
 
         mCxStack = nsnull;
 
         if (mJSPrincipals) {
-            JS_DropPrincipals(JS_GetRuntime(mCx), mJSPrincipals);
+            JS_DropPrincipals(rt, mJSPrincipals);
         }
 
-        JSRuntime* rt = gOldContextCallback ? JS_GetRuntime(mCx) : NULL;
-
         JS_EndRequest(mCx);
         JS_DestroyContext(mCx);
 
         if (gOldContextCallback) {
             NS_ASSERTION(rt, "Should never be null!");
             JS_SetContextCallback(rt, gOldContextCallback);
             gOldContextCallback = NULL;
         }
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -2812,18 +2812,18 @@ jsdService::EnumerateScripts (jsdIScript
 
     return rv;
 }
 
 NS_IMETHODIMP
 jsdService::GC (void)
 {
     ASSERT_VALID_CONTEXT;
-    JSContext *cx = JSD_GetDefaultJSContext (mCx);
-    JS_GC(cx);
+    JSRuntime *rt = JSD_GetJSRuntime (mCx);
+    JS_GC(rt);
     return NS_OK;
 }
     
 NS_IMETHODIMP
 jsdService::DumpHeap(const nsACString &fileName)
 {
     ASSERT_VALID_CONTEXT;
 #ifndef DEBUG
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.c
@@ -93,16 +93,23 @@ JSD_GetMinorVersion(void)
 
 JSD_PUBLIC_API(JSContext*)
 JSD_GetDefaultJSContext(JSDContext* jsdc)
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     return jsdc->dumbContext;
 }
 
+JSD_PUBLIC_API(JSRuntime*)
+JSD_GetJSRuntime(JSDContext* jsdc)
+{
+    JSD_ASSERT_VALID_CONTEXT(jsdc);
+    return jsdc->jsrt;
+}
+
 JSD_PUBLIC_API(void)
 JSD_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
 {
     jsd_SetUserCallbacks(jsrt, callbacks, user);
 }
 
 JSD_PUBLIC_API(void)
 JSD_JSContextInUse(JSDContext* jsdc, JSContext* context)
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -184,16 +184,22 @@ JSD_GetMinorVersion(void);
 
 /*
 * Returns a 'dumb' JSContext that can be used for utility purposes as needed
 */
 extern JSD_PUBLIC_API(JSContext*)
 JSD_GetDefaultJSContext(JSDContext* jsdc);
 
 /*
+* Returns a JSRuntime this context is associated with
+*/
+extern JSD_PUBLIC_API(JSRuntime*)
+JSD_GetJSRuntime(JSDContext* jsdc);
+
+/*
 * Set the private data for this context, returns previous value
 */
 extern JSD_PUBLIC_API(void *)
 JSD_SetContextPrivate(JSDContext *jsdc, void *data);
 
 /*
 * Get the private data for this context
 */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -42,17 +42,17 @@ GC(JSContext *cx, unsigned argc, jsval *
 #ifndef JS_MORE_DETERMINISTIC
     size_t preBytes = cx->runtime->gcBytes;
 #endif
 
     if (compartment)
         PrepareForDebugGC(cx->runtime);
     else
         PrepareForFullGC(cx->runtime);
-    GCForReason(cx, gcreason::API);
+    GCForReason(cx->runtime, gcreason::API);
 
     char buf[256] = { '\0' };
 #ifndef JS_MORE_DETERMINISTIC
     JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n",
                 (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes);
 #endif
     JSString *str = JS_NewStringCopyZ(cx, buf);
     if (!str)
@@ -252,17 +252,17 @@ GCSlice(JSContext *cx, unsigned argc, js
 
     if (argc == 1) {
         if (!JS_ValueToECMAUint32(cx, vp[2], &budget))
             return false;
     } else {
         limit = false;
     }
 
-    GCDebugSlice(cx, limit, budget);
+    GCDebugSlice(cx->runtime, limit, budget);
     *vp = JSVAL_VOID;
     return JS_TRUE;
 }
 
 static JSBool
 DeterministicGC(JSContext *cx, unsigned argc, jsval *vp)
 {
     if (argc != 1) {
@@ -493,17 +493,17 @@ MJitChunkLimit(JSContext *cx, unsigned a
         return JS_FALSE;
 
 #ifdef JS_METHODJIT
     mjit::SetChunkLimit((uint32_t) t);
 #endif
 
     // Clear out analysis information which might refer to code compiled with
     // the previous chunk limit.
-    JS_GC(cx);
+    JS_GC(cx->runtime);
 
     vp->setUndefined();
     return true;
 }
 
 static JSBool
 Terminate(JSContext *cx, unsigned arg, jsval *vp)
 {
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -33,17 +33,17 @@ wrap(JSContext *cx, JSObject *toWrap, JS
     if (!JS_WrapObject(cx, &wrapper))
         return NULL;
     return wrapper;
 }
 
 static JSObject *
 PreWrap(JSContext *cx, JSObject *scope, JSObject *obj, unsigned flags)
 {
-    JS_GC(cx);
+    JS_GC(JS_GetRuntime(cx));
     return obj;
 }
 
 static JSObject *
 Wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, unsigned flags)
 {
     return js::Wrapper::New(cx, obj, proto, parent, &js::CrossCompartmentWrapper::singleton);
 }
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -24,24 +24,24 @@ BEGIN_TEST(testConservativeGC)
 
     EVAL("String(Math.sqrt(3));", &tmp);
     CHECK(JSVAL_IS_STRING(tmp));
     JSString *str2 = JSVAL_TO_STRING(tmp);
     JSString str2Copy = *str2;
 
     tmp = JSVAL_NULL;
 
-    JS_GC(cx);
+    JS_GC(rt);
 
     EVAL("var a = [];\n"
          "for (var i = 0; i != 10000; ++i) {\n"
          "a.push(i + 0.1, [1, 2], String(Math.sqrt(i)), {a: i});\n"
          "}", &tmp);
 
-    JS_GC(cx);
+    JS_GC(rt);
 
     checkObjectFields((JSObject *)objCopy, JSVAL_TO_OBJECT(v2));
     CHECK(!memcmp(&strCopy, JSVAL_TO_STRING(v3), sizeof(strCopy)));
 
     checkObjectFields((JSObject *)obj2Copy, obj2);
     CHECK(!memcmp(&str2Copy, str2, sizeof(str2Copy)));
 
     return true;
@@ -64,15 +64,15 @@ BEGIN_TEST(testDerivedValues)
   static const jschar expected[] = { 'o', 'n', 'c', 'e' };
   const jschar *ch = JS_GetStringCharsZ(cx, str);
   str = NULL;
 
   /* Do a lot of allocation and collection. */
   for (int i = 0; i < 3; i++) {
     for (int j = 0; j < 1000; j++)
       JS_NewStringCopyZ(cx, "as I pondered weak and weary");
-    JS_GC(cx);
+    JS_GC(rt);
   }
 
   CHECK(!memcmp(ch, expected, sizeof(expected)));
   return true;
 }
 END_TEST(testDerivedValues)
--- a/js/src/jsapi-tests/testExternalStrings.cpp
+++ b/js/src/jsapi-tests/testExternalStrings.cpp
@@ -34,17 +34,17 @@ BEGIN_TEST(testExternalStrings)
     for (unsigned i = 0; i < N; ++i) {
         CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer1));
         CHECK(JS_NewExternalString(cx, arr, arrlen, &finalizer2));
     }
 
     // clear that newborn root
     JS_NewUCStringCopyN(cx, arr, arrlen);
 
-    JS_GC(cx);
+    JS_GC(rt);
 
     // a generous fudge factor to account for strings rooted by conservative gc
     const unsigned epsilon = 10;
 
     CHECK((N - finalized1) < epsilon);
     CHECK((N - finalized2) < epsilon);
 
     return true;
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -32,17 +32,17 @@ BEGIN_TEST(testGCOutOfMemory)
         "})();";
     JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1,
                                   root.addr());
 
     /* Check that we get OOM. */
     CHECK(!ok);
     CHECK(!JS_IsExceptionPending(cx));
     CHECK_EQUAL(errorCount, 1);
-    JS_GC(cx);
+    JS_GC(rt);
     EVAL("(function() {"
          "    var array = [];"
          "    for (var i = max >> 2; i != 0;) {"
          "        --i;"
          "        array.push({});"
          "    }"
          "})();", root.addr());
     CHECK_EQUAL(errorCount, 1);
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -31,13 +31,13 @@ FinalizeCallback(JSFreeOp *fop, JSFinali
 }
 
 BEGIN_TEST(testInternAcrossGC)
 {
     sw.str = JS_InternString(cx, "wrapped chars that another test shouldn't be using");
     sw.strOk = false;
     CHECK(sw.str);
     JS_SetFinalizeCallback(rt, FinalizeCallback);
-    JS_GC(cx);
+    JS_GC(rt);
     CHECK(sw.strOk);
     return true;
 }
 END_TEST(testInternAcrossGC)
--- a/js/src/jsapi-tests/testScriptObject.cpp
+++ b/js/src/jsapi-tests/testScriptObject.cpp
@@ -14,17 +14,17 @@ struct ScriptObjectFixture : public JSAP
         for (int i = 0; i < code_size; i++)
             uc_code[i] = code[i];
     }
 
     bool tryScript(JSScript *script)
     {
         CHECK(script);
 
-        JS_GC(cx);
+        JS_GC(rt);
 
         /* After a garbage collection, the script should still work. */
         jsval result;
         CHECK(JS_ExecuteScript(cx, global, script, &result));
 
         return true;
     }
 };
--- a/js/src/jsapi-tests/testTrap.cpp
+++ b/js/src/jsapi-tests/testTrap.cpp
@@ -6,17 +6,17 @@
 #include "jsdbgapi.h"
 
 static int emptyTrapCallCount = 0;
 
 static JSTrapStatus
 EmptyTrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
                  jsval closure)
 {
-    JS_GC(cx);
+    JS_GC(JS_GetRuntime(cx));
     if (JSVAL_IS_STRING(closure))
         ++emptyTrapCallCount;
     return JSTRAP_CONTINUE;
 }
 
 BEGIN_TEST(testTrap_gc)
 {
     static const char source[] =
@@ -54,25 +54,25 @@ BEGIN_TEST(testTrap_gc)
         jsbytecode *line6 = JS_LineNumberToPC(cx, script, 5);
         CHECK(line2);
 
         trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
         CHECK(trapClosure);
         JS_SetTrap(cx, script, line2, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
         JS_SetTrap(cx, script, line6, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
 
-        JS_GC(cx);
+        JS_GC(rt);
 
         CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
     }
 
     // execute
     CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
     CHECK_EQUAL(emptyTrapCallCount, 11);
 
-    JS_GC(cx);
+    JS_GC(rt);
 
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
 
     return true;
 }
 END_TEST(testTrap_gc)
 
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -197,17 +197,17 @@ BEGIN_TEST(testXDR_bug506491)
     script = FreezeThaw(cx, script);
     CHECK(script);
 
     // execute
     jsvalRoot v2(cx);
     CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
 
     // try to break the Block object that is the parent of f
-    JS_GC(cx);
+    JS_GC(rt);
 
     // confirm
     EVAL("f() === 'ok';\n", v2.addr());
     jsvalRoot trueval(cx, JSVAL_TRUE);
     CHECK_SAME(v2, trueval);
     return true;
 }
 END_TEST(testXDR_bug506491)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1132,35 +1132,29 @@ JS_SetContextCallback(JSRuntime *rt, JSC
     old = rt->cxCallback;
     rt->cxCallback = cxCallback;
     return old;
 }
 
 JS_PUBLIC_API(JSContext *)
 JS_NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
-    return js_NewContext(rt, stackChunkSize);
+    return NewContext(rt, stackChunkSize);
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext *cx)
 {
-    js_DestroyContext(cx, JSDCM_FORCE_GC);
+    DestroyContext(cx, DCM_FORCE_GC);
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyContextNoGC(JSContext *cx)
 {
-    js_DestroyContext(cx, JSDCM_NO_GC);
-}
-
-JS_PUBLIC_API(void)
-JS_DestroyContextMaybeGC(JSContext *cx)
-{
-    js_DestroyContext(cx, JSDCM_MAYBE_GC);
+    DestroyContext(cx, DCM_NO_GC);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetContextPrivate(JSContext *cx)
 {
     return cx->data;
 }
 
@@ -2855,21 +2849,21 @@ JS_DumpHeap(JSRuntime *rt, FILE *fp, voi
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsGCMarkingTracer(JSTracer *trc)
 {
     return IS_GC_MARKING_TRACER(trc);
 }
 
 JS_PUBLIC_API(void)
-JS_GC(JSContext *cx)
-{
-    AssertNoGC(cx);
-    PrepareForFullGC(cx->runtime);
-    GC(cx, GC_NORMAL, gcreason::API);
+JS_GC(JSRuntime *rt)
+{
+    AssertNoGC(rt);
+    PrepareForFullGC(rt);
+    GC(rt, GC_NORMAL, gcreason::API);
 }
 
 JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext *cx)
 {
     MaybeGC(cx);
 }
 
@@ -2956,21 +2950,16 @@ JS_SetGCParameterForThread(JSContext *cx
 
 JS_PUBLIC_API(uint32_t)
 JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key)
 {
     JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES);
     return 0;
 }
 
-JS_PUBLIC_API(void)
-JS_FlushCaches(JSContext *cx)
-{
-}
-
 JS_PUBLIC_API(JSString *)
 JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
                      const JSStringFinalizer *fin)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     JSString *s = JSExternalString::new_(cx, chars, length, fin);
     Probes::createString(cx, s, length);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2563,19 +2563,16 @@ extern JS_PUBLIC_API(JSContext *)
 JS_NewContext(JSRuntime *rt, size_t stackChunkSize);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_DestroyContextNoGC(JSContext *cx);
 
-extern JS_PUBLIC_API(void)
-JS_DestroyContextMaybeGC(JSContext *cx);
-
 extern JS_PUBLIC_API(void *)
 JS_GetContextPrivate(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_SetContextPrivate(JSContext *cx, void *data);
 
 extern JS_PUBLIC_API(void *)
 JS_GetSecondContextPrivate(JSContext *cx);
@@ -3153,22 +3150,16 @@ JS_DumpNamedRoots(JSRuntime *rt,
  * The root is pointed at by rp; if the root is unnamed, name is null; data is
  * supplied from the third parameter to JS_MapGCRoots.
  *
  * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently
  * enumerated root to be removed.  To stop enumeration, set JS_MAP_GCROOT_STOP
  * in the return value.  To keep on mapping, return JS_MAP_GCROOT_NEXT.  These
  * constants are flags; you can OR them together.
  *
- * This function acquires and releases rt's GC lock around the mapping of the
- * roots table, so the map function should run to completion in as few cycles
- * as possible.  Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest,
- * or any JS API entry point that acquires locks, without double-tripping or
- * deadlocking on the GC lock.
- *
  * The JSGCRootType parameter indicates whether rp is a pointer to a Value
  * (which is obtained by '(Value *)rp') or a pointer to a GC-thing pointer
  * (which is obtained by '(void **)rp').
  *
  * JS_MapGCRoots returns the count of roots that were successfully mapped.
  */
 #define JS_MAP_GCROOT_NEXT      0       /* continue mapping entries */
 #define JS_MAP_GCROOT_STOP      1       /* stop mapping entries */
@@ -3409,20 +3400,20 @@ JS_DumpHeap(JSRuntime *rt, FILE *fp, voi
             void *thingToFind, size_t maxDepth, void *thingToIgnore);
 
 #endif
 
 /*
  * Garbage collector API.
  */
 extern JS_PUBLIC_API(void)
-JS_GC(JSContext *cx);
+JS_GC(JSRuntime *rt);
 
 extern JS_PUBLIC_API(void)
-JS_CompartmentGC(JSContext *cx, JSCompartment *comp);
+JS_CompartmentGC(JSRuntime *rt, JSCompartment *comp);
 
 extern JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb);
 
 extern JS_PUBLIC_API(void)
@@ -3488,25 +3479,16 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key);
 
 /*
- * Flush the code cache for the current thread. The operation might be
- * delayed if the cache cannot be flushed currently because native
- * code is currently executing.
- */
-
-extern JS_PUBLIC_API(void)
-JS_FlushCaches(JSContext *cx);
-
-/*
  * Create a new JSString whose chars member refers to external memory, i.e.,
  * memory requiring application-specific finalization.
  */
 extern JS_PUBLIC_API(JSString *)
 JS_NewExternalString(JSContext *cx, const jschar *chars, size_t length,
                      const JSStringFinalizer *fin);
 
 /*
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -164,17 +164,17 @@ JSRuntime::createBumpPointerAllocator(JS
 
 JSScript *
 js_GetCurrentScript(JSContext *cx)
 {
     return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
 }
 
 JSContext *
-js_NewContext(JSRuntime *rt, size_t stackChunkSize)
+js::NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JS_AbortIfWrongThread(rt);
 
     JSContext *cx = OffTheBooks::new_<JSContext>(rt);
     if (!cx)
         return NULL;
 
     JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
@@ -193,58 +193,58 @@ js_NewContext(JSRuntime *rt, size_t stac
 
     js_InitRandom(cx);
 
     /*
      * If cx is the first context on this runtime, initialize well-known atoms,
      * keywords, numbers, and strings.  If one of these steps should fail, the
      * runtime will be left in a partially initialized state, with zeroes and
      * nulls stored in the default-initialized remainder of the struct.  We'll
-     * clean the runtime up under js_DestroyContext, because cx will be "last"
+     * clean the runtime up under DestroyContext, because cx will be "last"
      * as well as "first".
      */
     if (first) {
 #ifdef JS_THREADSAFE
         JS_BeginRequest(cx);
 #endif
         bool ok = rt->staticStrings.init(cx);
         if (ok)
             ok = InitCommonAtoms(cx);
 
 #ifdef JS_THREADSAFE
         JS_EndRequest(cx);
 #endif
         if (!ok) {
-            js_DestroyContext(cx, JSDCM_NEW_FAILED);
+            DestroyContext(cx, DCM_NEW_FAILED);
             return NULL;
         }
     }
 
     JSContextCallback cxCallback = rt->cxCallback;
     if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
-        js_DestroyContext(cx, JSDCM_NEW_FAILED);
+        DestroyContext(cx, DCM_NEW_FAILED);
         return NULL;
     }
 
     return cx;
 }
 
 void
-js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
+js::DestroyContext(JSContext *cx, DestroyContextMode mode)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
     JS_ASSERT(!cx->enumerators);
 
 #ifdef JS_THREADSAFE
     JS_ASSERT(cx->outstandingRequests == 0);
 #endif
 
-    if (mode != JSDCM_NEW_FAILED) {
+    if (mode != DCM_NEW_FAILED) {
         if (JSContextCallback cxCallback = rt->cxCallback) {
             /*
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
             JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY));
         }
     }
@@ -265,26 +265,22 @@ js_DestroyContext(JSContext *cx, JSDestr
         FinishCommonAtoms(rt);
 
         /* Clear debugging state to remove GC roots. */
         for (CompartmentsIter c(rt); !c.done(); c.next())
             c->clearTraps(rt->defaultFreeOp());
         JS_ClearAllWatchPoints(cx);
 
         PrepareForFullGC(rt);
-        GC(cx, GC_NORMAL, gcreason::LAST_CONTEXT);
-    } else if (mode == JSDCM_FORCE_GC) {
+        GC(rt, GC_NORMAL, gcreason::LAST_CONTEXT);
+    } else if (mode == DCM_FORCE_GC) {
         JS_ASSERT(!rt->gcRunning);
         PrepareForFullGC(rt);
-        GC(cx, GC_NORMAL, gcreason::DESTROY_CONTEXT);
-    } else if (mode == JSDCM_MAYBE_GC) {
-        JS_ASSERT(!rt->gcRunning);
-        JS_MaybeGC(cx);
+        GC(rt, GC_NORMAL, gcreason::DESTROY_CONTEXT);
     }
-
     Foreground::delete_(cx);
 }
 
 namespace js {
 
 bool
 AutoResolving::alreadyStartedSlow() const
 {
@@ -866,40 +862,24 @@ js_InvokeOperationCallback(JSContext *cx
     /*
      * Reset the callback counter first, then run GC and yield. If another
      * thread is racing us here we will accumulate another callback request
      * which will be serviced at the next opportunity.
      */
     JS_ATOMIC_SET(&rt->interrupt, 0);
 
     if (rt->gcIsNeeded)
-        GCSlice(cx, GC_NORMAL, rt->gcTriggerReason);
-
-#ifdef JS_THREADSAFE
-    /*
-     * We automatically yield the current context every time the operation
-     * callback is hit since we might be called as a result of an impending
-     * GC on another thread, which would deadlock if we do not yield.
-     * Operation callbacks are supposed to happen rarely (seconds, not
-     * milliseconds) so it is acceptable to yield at every callback.
-     *
-     * As the GC can be canceled before it does any request checks we yield
-     * even if rt->gcIsNeeded was true above. See bug 590533.
-     */
-    JS_YieldRequest(cx);
-#endif
-
-    JSOperationCallback cb = cx->operationCallback;
+        GCSlice(rt, GC_NORMAL, rt->gcTriggerReason);
 
     /*
      * Important: Additional callbacks can occur inside the callback handler
      * if it re-enters the JS engine. The embedding must ensure that the
      * callback is disconnected before attempting such re-entry.
      */
-
+    JSOperationCallback cb = cx->operationCallback;
     return !cb || cb(cx);
 }
 
 JSBool
 js_HandleExecutionInterrupt(JSContext *cx)
 {
     JSBool result = JS_TRUE;
     if (cx->runtime->interrupt)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -141,25 +141,18 @@ struct ConservativeGCData
      */
     uintptr_t           *nativeStackTop;
 
     union {
         jmp_buf         jmpbuf;
         uintptr_t       words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
     } registerSnapshot;
 
-    /*
-     * Cycle collector uses this to communicate that the native stack of the
-     * GC thread should be scanned only if the thread have more than the given
-     * threshold of requests.
-     */
-    unsigned requestThreshold;
-
     ConservativeGCData()
-      : nativeStackTop(NULL), requestThreshold(0)
+      : nativeStackTop(NULL)
     {}
 
     ~ConservativeGCData() {
 #ifdef JS_THREADSAFE
         /*
          * The conservative GC scanner should be disabled when the thread leaves
          * the last request.
          */
@@ -1449,34 +1442,33 @@ public:
         return get();
     }
 
     JSContext *operator ->() const {
         return get();
     }
 };
 
-} /* namespace js */
-
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
 extern JSContext *
-js_NewContext(JSRuntime *rt, size_t stackChunkSize);
+NewContext(JSRuntime *rt, size_t stackChunkSize);
 
-typedef enum JSDestroyContextMode {
-    JSDCM_NO_GC,
-    JSDCM_MAYBE_GC,
-    JSDCM_FORCE_GC,
-    JSDCM_NEW_FAILED
-} JSDestroyContextMode;
+enum DestroyContextMode {
+    DCM_NO_GC,
+    DCM_FORCE_GC,
+    DCM_NEW_FAILED
+};
 
 extern void
-js_DestroyContext(JSContext *cx, JSDestroyContextMode mode);
+DestroyContext(JSContext *cx, DestroyContextMode mode);
+
+} /* namespace js */
 
 #ifdef va_start
 extern JSBool
 js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap);
 
 extern JSBool
 js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
                        void *userRef, const unsigned errorNumber,
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -140,31 +140,31 @@ js::PrepareCompartmentForGC(JSCompartmen
 JS_FRIEND_API(void)
 js::PrepareForFullGC(JSRuntime *rt)
 {
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->scheduleGC();
 }
 
 JS_FRIEND_API(void)
-js::GCForReason(JSContext *cx, gcreason::Reason reason)
+js::GCForReason(JSRuntime *rt, gcreason::Reason reason)
 {
-    GC(cx, GC_NORMAL, reason);
+    GC(rt, GC_NORMAL, reason);
 }
 
 JS_FRIEND_API(void)
-js::ShrinkingGC(JSContext *cx, gcreason::Reason reason)
+js::ShrinkingGC(JSRuntime *rt, gcreason::Reason reason)
 {
-    GC(cx, GC_SHRINK, reason);
+    GC(rt, GC_SHRINK, reason);
 }
 
 JS_FRIEND_API(void)
-js::IncrementalGC(JSContext *cx, gcreason::Reason reason)
+js::IncrementalGC(JSRuntime *rt, gcreason::Reason reason)
 {
-    GCSlice(cx, GC_NORMAL, reason);
+    GCSlice(rt, GC_NORMAL, reason);
 }
 
 JS_FRIEND_API(void)
 JS_ShrinkGCBuffers(JSRuntime *rt)
 {
     ShrinkGCBuffers(rt);
 }
 
@@ -649,36 +649,16 @@ GetOwnerThread(const JSContext *cx)
     return cx->runtime->ownerThread();
 }
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx)
 {
     return cx->outstandingRequests;
 }
-
-AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
-                                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : context(cx)
-{
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-
-    JSRuntime *rt = context->runtime;
-    JS_ASSERT(rt->requestDepth >= 1);
-    JS_ASSERT(!rt->conservativeGC.requestThreshold);
-    if (rt->requestDepth == 1)
-        rt->conservativeGC.requestThreshold = 1;
-}
-
-AutoSkipConservativeScan::~AutoSkipConservativeScan()
-{
-    JSRuntime *rt = context->runtime;
-    if (rt->requestDepth == 1)
-        rt->conservativeGC.requestThreshold = 0;
-}
 #endif
 
 JS_FRIEND_API(JSCompartment *)
 GetContextCompartment(const JSContext *cx)
 {
     return cx->compartment;
 }
 
@@ -728,50 +708,36 @@ GCDescription::formatMessage(JSRuntime *
 }
 
 jschar *
 GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const
 {
     return rt->gcStats.formatJSON(timestamp);
 }
 
-JS_FRIEND_API(bool)
-WantGCSlice(JSRuntime *rt)
+JS_FRIEND_API(void)
+NotifyDidPaint(JSRuntime *rt)
 {
-    if (rt->gcZeal() == gc::ZealFrameVerifierValue || rt->gcZeal() == gc::ZealFrameGCValue)
-        return true;
-
-    if (rt->gcIncrementalState != gc::NO_INCREMENTAL)
-        return true;
-
-    return false;
-}
-
-JS_FRIEND_API(void)
-NotifyDidPaint(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime;
-
     if (rt->gcZeal() == gc::ZealFrameVerifierValue) {
         gc::VerifyBarriers(rt);
         return;
     }
 
     if (rt->gcZeal() == gc::ZealFrameGCValue) {
         PrepareForFullGC(rt);
-        GCSlice(cx, GC_NORMAL, gcreason::REFRESH_FRAME);
+        GCSlice(rt, GC_NORMAL, gcreason::REFRESH_FRAME);
         return;
     }
 
     if (rt->gcIncrementalState != gc::NO_INCREMENTAL && !rt->gcInterFrameGC) {
         for (CompartmentsIter c(rt); !c.done(); c.next()) {
             if (c->needsBarrier())
                 PrepareCompartmentForGC(c);
         }
-        GCSlice(cx, GC_NORMAL, gcreason::REFRESH_FRAME);
+        GCSlice(rt, GC_NORMAL, gcreason::REFRESH_FRAME);
     }
 
     rt->gcInterFrameGC = false;
 }
 
 extern JS_FRIEND_API(bool)
 IsIncrementalGCEnabled(JSRuntime *rt)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -564,27 +564,16 @@ JS_FRIEND_API(JSString *)
 GetPCCountScriptContents(JSContext *cx, size_t script);
 
 #ifdef JS_THREADSAFE
 JS_FRIEND_API(void *)
 GetOwnerThread(const JSContext *cx);
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx);
-
-class JS_FRIEND_API(AutoSkipConservativeScan)
-{
-  public:
-    AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
-    ~AutoSkipConservativeScan();
-
-  private:
-    JSContext *context;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
 #endif
 
 JS_FRIEND_API(JSCompartment *)
 GetContextCompartment(const JSContext *cx);
 
 JS_FRIEND_API(bool)
 HasUnrootedGlobal(const JSContext *cx);
 
@@ -675,23 +664,23 @@ PrepareForFullGC(JSRuntime *rt);
 /*
  * When triggering a GC using one of the functions below, it is first necessary
  * to select the compartments to be collected. To do this, you can call
  * PrepareCompartmentForGC on each compartment, or you can call PrepareForFullGC
  * to select all compartments. Failing to select any compartment is an error.
  */
 
 extern JS_FRIEND_API(void)
-GCForReason(JSContext *cx, gcreason::Reason reason);
+GCForReason(JSRuntime *rt, gcreason::Reason reason);
 
 extern JS_FRIEND_API(void)
-ShrinkingGC(JSContext *cx, gcreason::Reason reason);
+ShrinkingGC(JSRuntime *rt, gcreason::Reason reason);
 
 extern JS_FRIEND_API(void)
-IncrementalGC(JSContext *cx, gcreason::Reason reason);
+IncrementalGC(JSRuntime *rt, gcreason::Reason reason);
 
 extern JS_FRIEND_API(void)
 SetGCSliceTimeBudget(JSContext *cx, int64_t millis);
 
 enum GCProgress {
     /*
      * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END
      * callbacks. During an incremental GC, the sequence of callbacks is as
@@ -719,25 +708,22 @@ struct JS_FRIEND_API(GCDescription) {
 };
 
 typedef void
 (* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc);
 
 extern JS_FRIEND_API(GCSliceCallback)
 SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
 
-extern JS_FRIEND_API(bool)
-WantGCSlice(JSRuntime *rt);
-
 /*
  * Signals a good place to do an incremental slice, because the browser is
  * drawing a frame.
  */
 extern JS_FRIEND_API(void)
-NotifyDidPaint(JSContext *cx);
+NotifyDidPaint(JSRuntime *rt);
 
 extern JS_FRIEND_API(bool)
 IsIncrementalGCEnabled(JSRuntime *rt);
 
 extern JS_FRIEND_API(void)
 DisableIncrementalGC(JSRuntime *rt);
 
 extern JS_FRIEND_API(bool)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1108,17 +1108,17 @@ MarkConservativeStackRoots(JSTracer *trc
     if (rt->gcIncrementalState == MARK_ROOTS)
         rt->gcSavedRoots.clearAndFree();
 #endif
 
     ConservativeGCData *cgcd = &rt->conservativeGC;
     if (!cgcd->hasStackToScan()) {
 #ifdef JS_THREADSAFE
         JS_ASSERT(!rt->suspendCount);
-        JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold);
+        JS_ASSERT(!rt->requestDepth);
 #endif
         return;
     }
 
     uintptr_t *stackMin, *stackEnd;
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackMin = rt->nativeStackBase;
     stackEnd = cgcd->nativeStackTop;
@@ -1176,18 +1176,17 @@ ConservativeGCData::recordStackTop()
 
 static void
 RecordNativeStackTopForGC(JSRuntime *rt)
 {
     ConservativeGCData *cgcd = &rt->conservativeGC;
 
 #ifdef JS_THREADSAFE
     /* Record the stack top here only if we are called from a request. */
-    JS_ASSERT(rt->requestDepth >= cgcd->requestThreshold);
-    if (rt->requestDepth == cgcd->requestThreshold)
+    if (!rt->requestDepth)
         return;
 #endif
     cgcd->recordStackTop();
 }
 
 } /* namespace js */
 
 bool
@@ -1644,17 +1643,17 @@ ArenaLists::finalizeScripts(FreeOp *fop)
 
 static void
 RunLastDitchGC(JSContext *cx, gcreason::Reason reason)
 {
     JSRuntime *rt = cx->runtime;
 
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
-    GC(cx, GC_NORMAL, reason);
+    GC(rt, GC_NORMAL, reason);
 }
 
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
     JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty());
 
     JSCompartment *comp = cx->compartment;
@@ -2390,53 +2389,53 @@ TriggerCompartmentGC(JSCompartment *comp
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
     if (rt->gcZeal() == ZealAllocValue || rt->gcZeal() == ZealPokeValue) {
         PrepareForFullGC(rt);
-        GC(cx, GC_NORMAL, gcreason::MAYBEGC);
+        GC(rt, GC_NORMAL, gcreason::MAYBEGC);
+        return;
+    }
+
+    if (rt->gcIsNeeded) {
+        GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
     JSCompartment *comp = cx->compartment;
-    if (rt->gcIsNeeded) {
-        GCSlice(cx, GC_NORMAL, gcreason::MAYBEGC);
-        return;
-    }
-
     if (comp->gcBytes > 8192 &&
         comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4) &&
         rt->gcIncrementalState == NO_INCREMENTAL)
     {
         PrepareCompartmentForGC(comp);
-        GCSlice(cx, GC_NORMAL, gcreason::MAYBEGC);
+        GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
     if (comp->gcMallocAndFreeBytes > comp->gcTriggerMallocAndFreeBytes) {
         PrepareCompartmentForGC(comp);
-        GCSlice(cx, GC_NORMAL, gcreason::MAYBEGC);
+        GCSlice(rt, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
     /*
      * Access to the counters and, on 32 bit, setting gcNextFullGCTime below
      * is not atomic and a race condition could trigger or suppress the GC. We
      * tolerate this.
      */
     int64_t now = PRMJ_Now();
     if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
         if (rt->gcChunkAllocationSinceLastGC ||
             rt->gcNumArenasFreeCommitted > FreeCommittedArenasThreshold)
         {
             PrepareForFullGC(rt);
-            GCSlice(cx, GC_SHRINK, gcreason::MAYBEGC);
+            GCSlice(rt, GC_SHRINK, gcreason::MAYBEGC);
         } else {
             rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 }
 
 static void
 DecommitArenasFromAvailableList(JSRuntime *rt, Chunk **availableListHeadp)
@@ -3697,33 +3696,33 @@ Collect(JSRuntime *rt, bool incremental,
          * stop creating garbage.
          */
     } while (!rt->hasContexts() && rt->gcPoke);
 }
 
 namespace js {
 
 void
-GC(JSContext *cx, JSGCInvocationKind gckind, gcreason::Reason reason)
+GC(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
-    Collect(cx->runtime, false, SliceBudget::Unlimited, gckind, reason);
+    Collect(rt, false, SliceBudget::Unlimited, gckind, reason);
 }
 
 void
-GCSlice(JSContext *cx, JSGCInvocationKind gckind, gcreason::Reason reason)
+GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
-    Collect(cx->runtime, true, cx->runtime->gcSliceBudget, gckind, reason);
+    Collect(rt, true, rt->gcSliceBudget, gckind, reason);
 }
 
 void
-GCDebugSlice(JSContext *cx, bool limit, int64_t objCount)
+GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount)
 {
     int64_t budget = limit ? SliceBudget::WorkBudget(objCount) : SliceBudget::Unlimited;
-    PrepareForDebugGC(cx->runtime);
-    Collect(cx->runtime, true, budget, GC_NORMAL, gcreason::API);
+    PrepareForDebugGC(rt);
+    Collect(rt, true, budget, GC_NORMAL, gcreason::API);
 }
 
 /* Schedule a full GC unless a compartment will already be collected. */
 void
 PrepareForDebugGC(JSRuntime *rt)
 {
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (c->isGCScheduled())
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1392,23 +1392,23 @@ typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
 
     /* Minimize GC triggers and release empty GC chunks right away. */
     GC_SHRINK             = 1
 } JSGCInvocationKind;
 
 extern void
-GC(JSContext *cx, JSGCInvocationKind gckind, js::gcreason::Reason reason);
+GC(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason);
 
 extern void
-GCSlice(JSContext *cx, JSGCInvocationKind gckind, js::gcreason::Reason reason);
+GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, js::gcreason::Reason reason);
 
 extern void
-GCDebugSlice(JSContext *cx, bool limit, int64_t objCount);
+GCDebugSlice(JSRuntime *rt, bool limit, int64_t objCount);
 
 extern void
 PrepareForDebugGC(JSRuntime *rt);
 
 } /* namespace js */
 
 namespace js {
 
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -256,39 +256,35 @@ interface nsIXPCComponents_Utils : nsISu
     [implicit_jscontext]
     xpcIJSWeakReference getWeakReference(in jsval obj);
 
     /*
      * To be called from JS only.
      *
      * Force an immediate garbage collection cycle.
      */
-    [implicit_jscontext]
     void forceGC();
     
     /*
      * To be called from JS only.
      *
      * Force an immediate shrinking garbage collection cycle.
      */
-    [implicit_jscontext]
     void forceShrinkingGC();
 
     /*
      * Schedule a garbage collection cycle for a point in the future when no JS
      * is running. Call the provided function once this has occurred.
      */
-    [implicit_jscontext]
     void schedulePreciseGC(in ScheduledGCCallback callback);
     
     /*
      * Schedule a shrinking garbage collection cycle for a point in the future
      * when no JS is running. Call the provided function once this has occured.
      */
-    [implicit_jscontext]
     void schedulePreciseShrinkingGC(in ScheduledGCCallback callback);
 
     /**
      * Return the keys in a weak map.  This operation is
      * non-deterministic because it is affected by the scheduling of the
      * garbage collector and the cycle collector.
      *
      * This should only be used to write tests of the interaction of
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -547,19 +547,20 @@ DumpXPC(JSContext *cx, unsigned argc, js
         xpc->DebugDump(int16_t(depth));
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 static JSBool
 GC(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JS_GC(cx);
+    JSRuntime *rt = JS_GetRuntime(cx);
+    JS_GC(rt);
 #ifdef JS_GCMETER
-    js_DumpGCStats(JS_GetRuntime(cx), stdout);
+    js_DumpGCStats(rt, stdout);
 #endif
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 #ifdef JS_GC_ZEAL
 static JSBool
 GCZeal(JSContext *cx, unsigned argc, jsval *vp)
@@ -2008,22 +2009,22 @@ main(int argc, char **argv, char **envp)
 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
             // test of late call and release (see below)
             nsCOMPtr<nsIJSContextStack> bogus;
             xpc->WrapJS(cx, glob, NS_GET_IID(nsIJSContextStack),
                         (void**) getter_AddRefs(bogus));
 #endif
             JS_DropPrincipals(rt, gJSPrincipals);
             JS_ClearScope(cx, glob);
-            JS_GC(cx);
+            JS_GC(rt);
             JSContext *oldcx;
             cxstack->Pop(&oldcx);
             NS_ASSERTION(oldcx == cx, "JS thread context push/pop mismatch");
             cxstack = nsnull;
-            JS_GC(cx);
+            JS_GC(rt);
         } //this scopes the JSAutoCrossCompartmentCall
         JS_EndRequest(cx);
         JS_DestroyContext(cx);
     } // this scopes the nsCOMPtrs
 
     if (!XRE_ShutdownTestShell())
         NS_ERROR("problem shutting down testshell");
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3613,84 +3613,80 @@ nsXPCComponents_Utils::GetWeakReference(
     nsresult rv = ref->Init(cx, object);
     NS_ENSURE_SUCCESS(rv, rv);
     ref.forget(_retval);
     return NS_OK;
 }
 
 /* void forceGC (); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::ForceGC(JSContext *cx)
+nsXPCComponents_Utils::ForceGC()
 {
-    js::PrepareForFullGC(JS_GetRuntime(cx));
-    js::GCForReason(cx, js::gcreason::COMPONENT_UTILS);
+    JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+    js::PrepareForFullGC(rt);
+    js::GCForReason(rt, js::gcreason::COMPONENT_UTILS);
     return NS_OK;
 }
 
 /* void forceShrinkingGC (); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::ForceShrinkingGC(JSContext *cx)
+nsXPCComponents_Utils::ForceShrinkingGC()
 {
-    js::PrepareForFullGC(JS_GetRuntime(cx));
-    js::ShrinkingGC(cx, js::gcreason::COMPONENT_UTILS);
+    JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+    js::PrepareForFullGC(rt);
+    js::ShrinkingGC(rt, js::gcreason::COMPONENT_UTILS);
     return NS_OK;
 }
 
 class PreciseGCRunnable : public nsRunnable
 {
   public:
-    PreciseGCRunnable(JSContext *aCx, ScheduledGCCallback* aCallback, bool aShrinking)
-    : mCallback(aCallback), mCx(aCx), mShrinking(aShrinking) {}
+    PreciseGCRunnable(ScheduledGCCallback* aCallback, bool aShrinking)
+    : mCallback(aCallback), mShrinking(aShrinking) {}
 
     NS_IMETHOD Run()
     {
-        nsCOMPtr<nsIJSRuntimeService> runtimeSvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-        NS_ENSURE_STATE(runtimeSvc);
-
-        JSRuntime* rt = nsnull;
-        runtimeSvc->GetRuntime(&rt);
-        NS_ENSURE_STATE(rt);
+        JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
 
         JSContext *cx;
         JSContext *iter = nsnull;
         while ((cx = JS_ContextIterator(rt, &iter)) != NULL) {
             if (JS_IsRunning(cx)) {
                 return NS_DispatchToMainThread(this);
             }
         }
 
-        js::PrepareForFullGC(JS_GetRuntime(mCx));
+        js::PrepareForFullGC(rt);
         if (mShrinking)
-            js::ShrinkingGC(mCx, js::gcreason::COMPONENT_UTILS);
+            js::ShrinkingGC(rt, js::gcreason::COMPONENT_UTILS);
         else
-            js::GCForReason(mCx, js::gcreason::COMPONENT_UTILS);
+            js::GCForReason(rt, js::gcreason::COMPONENT_UTILS);
 
         mCallback->Callback();
         return NS_OK;
     }
 
   private:
     nsRefPtr<ScheduledGCCallback> mCallback;
-    JSContext *mCx;
     bool mShrinking;
 };
 
-/* [inline_jscontext] void schedulePreciseGC(in ScheduledGCCallback callback); */
+/* void schedulePreciseGC(in ScheduledGCCallback callback); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::SchedulePreciseGC(ScheduledGCCallback* aCallback, JSContext* aCx)
+nsXPCComponents_Utils::SchedulePreciseGC(ScheduledGCCallback* aCallback)
 {
-    nsRefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCx, aCallback, false);
+    nsRefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCallback, false);
     return NS_DispatchToMainThread(event);
 }
 
-/* [inline_jscontext] void schedulePreciseShrinkingGC(in ScheduledGCCallback callback); */
+/* void schedulePreciseShrinkingGC(in ScheduledGCCallback callback); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::SchedulePreciseShrinkingGC(ScheduledGCCallback* aCallback, JSContext* aCx)
+nsXPCComponents_Utils::SchedulePreciseShrinkingGC(ScheduledGCCallback* aCallback)
 {
-    nsRefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCx, aCallback, true);
+    nsRefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCallback, true);
     return NS_DispatchToMainThread(event);
 }
 
 /* [implicit_jscontext] jsval nondeterministicGetWeakMapKeys(in jsval aMap); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::NondeterministicGetWeakMapKeys(const jsval &aMap,
                                                       JSContext *aCx,
                                                       jsval *aKeys)
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -404,39 +404,28 @@ nsXPConnect::Collect(PRUint32 reason, PR
     // will already be marked by the JS GC and will thus be colored black
     // themselves. Any C++ objects they hold will have a missing (untraversed)
     // edge from the JS object to the C++ object and so it will be marked black
     // too. This decreases the number of objects that the cycle collector has to
     // deal with.
     // To improve debugging, if DEBUG_CC is defined all JS objects are
     // traversed.
 
-    XPCCallContext ccx(NATIVE_CALLER);
-    if (!ccx.IsValid())
-        return;
-
-    JSContext *cx = ccx.GetJSContext();
-    JSRuntime *rt = GetRuntime()->GetJSRuntime();
-
-    // We want to scan the current thread for GC roots only if it was in a
-    // request prior to the Collect call to avoid false positives during the
-    // cycle collection. So to compensate for JS_BeginRequest in
-    // XPCCallContext::Init we disable the conservative scanner if that call
-    // has started the request on this thread.
-    js::AutoSkipConservativeScan ascs(cx);
     MOZ_ASSERT(reason < js::gcreason::NUM_REASONS);
     js::gcreason::Reason gcreason = (js::gcreason::Reason)reason;
+
+    JSRuntime *rt = GetRuntime()->GetJSRuntime();
     js::PrepareForFullGC(rt);
     if (kind == nsGCShrinking) {
-        js::ShrinkingGC(cx, gcreason);
+        js::ShrinkingGC(rt, gcreason);
     } else if (kind == nsGCIncremental) {
-        js::IncrementalGC(cx, gcreason);
+        js::IncrementalGC(rt, gcreason);
     } else {
         MOZ_ASSERT(kind == nsGCNormal);
-        js::GCForReason(cx, gcreason);
+        js::GCForReason(rt, gcreason);
     }
 }
 
 NS_IMETHODIMP
 nsXPConnect::GarbageCollect(PRUint32 reason, PRUint32 kind)
 {
     Collect(reason, kind);
     return NS_OK;
@@ -2798,27 +2787,17 @@ nsXPConnect::GetTelemetryValue(JSContext
 
     *rval = OBJECT_TO_JSVAL(obj);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::NotifyDidPaint()
 {
-    JSRuntime *rt = mRuntime->GetJSRuntime();
-    if (!js::WantGCSlice(rt))
-        return NS_OK;
-
-    XPCCallContext ccx(NATIVE_CALLER);
-    if (!ccx.IsValid())
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    JSContext *cx = ccx.GetJSContext();
-
-    js::NotifyDidPaint(cx);
+    js::NotifyDidPaint(GetRuntime()->GetJSRuntime());
     return NS_OK;
 }
 
 const PRUint8 HAS_PRINCIPALS_FLAG               = 1;
 const PRUint8 HAS_ORIGIN_PRINCIPALS_FLAG        = 2;
 
 static nsresult
 WriteScriptOrFunction(nsIObjectOutputStream *stream, JSContext *cx,
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -40,17 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAppRunner.h"
 #include "nsToolkitCompsCID.h"
 #include "nsXREDirProvider.h"
 
 #include "jsapi.h"
 
-#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
 #include "nsIAppStartup.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsILocalFile.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIProfileChangeStatus.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIToolkitChromeRegistry.h"
@@ -832,24 +832,25 @@ nsXREDirProvider::DoShutdown()
       nsCOMPtr<nsIProfileChangeStatus> cs = new ProfileChangeStatusImpl();
       static const PRUnichar kShutdownPersist[] =
         {'s','h','u','t','d','o','w','n','-','p','e','r','s','i','s','t','\0'};
       obsSvc->NotifyObservers(cs, "profile-change-net-teardown", kShutdownPersist);
       obsSvc->NotifyObservers(cs, "profile-change-teardown", kShutdownPersist);
 
       // Phase 2c: Now that things are torn down, force JS GC so that things which depend on
       // resources which are about to go away in "profile-before-change" are destroyed first.
-      nsCOMPtr<nsIThreadJSContextStack> stack
-        (do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
-      if (stack)
+
+      nsCOMPtr<nsIJSRuntimeService> rtsvc
+        (do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+      if (rtsvc)
       {
-        JSContext *cx = nsnull;
-        stack->GetSafeJSContext(&cx);
-        if (cx)
-          ::JS_GC(cx);
+        JSRuntime *rt = nsnull;
+        rtsvc->GetRuntime(&rt);
+        if (rt)
+          ::JS_GC(rt);
       }
 
       // Phase 3: Notify observers of a profile change
       obsSvc->NotifyObservers(cs, "profile-before-change", kShutdownPersist);
     }
     mProfileNotified = false;
   }
 }