Bug 890686 - Implement JS_GlobalObjectTraceHook, and install it appropriately. (r=billm)
authorEric Faust <efaustbmo@gmail.com>
Fri, 04 Apr 2014 16:58:08 -0700
changeset 189205 68984d5a66a3e2fbacf06dbcdf1e144efe2108a1
parent 189204 bf1c135138c4f268ed64e10f484e8b4560ba7f3c
child 189206 7078ef898123950d8550436e3f19f5d17604dcf1
push idunknown
push userunknown
push dateunknown
reviewersbillm
bugs890686
milestone31.0a1
Bug 890686 - Implement JS_GlobalObjectTraceHook, and install it appropriately. (r=billm)
content/xul/document/src/nsXULPrototypeDocument.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/indexedDB/IDBObjectStore.cpp
dom/xbl/nsXBLDocumentInfo.cpp
js/jsd/jsd_high.cpp
js/src/gc/Marking.cpp
js/src/gdb/gdb-tests.cpp
js/src/jsapi-tests/testChromeBuffer.cpp
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsworkers.cpp
js/src/shell/js.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/SelfHosting.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCJSContextStack.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/xpcprivate.h
netwerk/base/src/ProxyAutoConfig.cpp
tools/profiler/SaveProfileTask.cpp
xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -95,17 +95,18 @@ nsXULPDGlobalObject_resolve(JSContext *c
 
 
 const JSClass nsXULPDGlobalObject::gSharedGlobalClass = {
     "nsXULPrototypeScript compilation scope",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0),
     JS_PropertyStub,  JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, nsXULPDGlobalObject_resolve,  JS_ConvertStub,
-    nsXULPDGlobalObject_finalize, nullptr, nullptr, nullptr, nullptr
+    nsXULPDGlobalObject_finalize, nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 
 
 //----------------------------------------------------------------------
 //
 // ctors, dtors, n' stuff
 //
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2549,16 +2549,18 @@ template <class T, JS::Handle<JSObject*>
                                                         JS::Handle<JSObject*>)>
 JSObject*
 CreateGlobal(JSContext* aCx, T* aObject, nsWrapperCache* aCache,
              const JSClass* aClass, JS::CompartmentOptions& aOptions,
              JSPrincipals* aPrincipal)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
+  aOptions.setTrace(TraceGlobal);
+
   JS::Rooted<JSObject*> global(aCx,
     JS_NewGlobalObject(aCx, aClass, aPrincipal, JS::DontFireOnNewGlobalHook,
                        aOptions));
   if (!global) {
     NS_WARNING("Failed to create global");
     return nullptr;
   }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -232,17 +232,17 @@ class CGDOMJSClass(CGThing):
 
     def define(self):
         traceHook = 'nullptr'
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
         classFlags = "JSCLASS_IS_DOMJSCLASS | "
         if self.descriptor.interface.getExtendedAttribute("Global"):
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
-            traceHook = "mozilla::dom::TraceGlobal"
+            traceHook = "JS_GlobalObjectTraceHook"
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
             newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.interface.getExtendedAttribute("Global"):
             newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -680,17 +680,19 @@ class ThreadLocalJSRuntime
     }
   }
 };
 
 const JSClass ThreadLocalJSRuntime::sGlobalClass = {
   "IndexedDBTransactionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+  nullptr, nullptr, nullptr, nullptr,
+  JS_GlobalObjectTraceHook
 };
 
 inline
 already_AddRefed<IDBRequest>
 GenerateRequest(IDBObjectStore* aObjectStore)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   IDBDatabase* database = aObjectStore->Transaction()->Database();
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -92,17 +92,17 @@ nsXBLDocGlobalObject_resolve(JSContext *
 const JSClass nsXBLDocGlobalObject::gSharedGlobalClass = {
     "nsXBLPrototypeScript compilation scope",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0),
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, nsXBLDocGlobalObject_resolve,
     JS_ConvertStub, nsXBLDocGlobalObject_finalize,
-    nullptr, nullptr, nullptr, nullptr
+    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
 };
 
 //----------------------------------------------------------------------
 //
 // nsXBLDocGlobalObject
 //
 
 nsXBLDocGlobalObject::nsXBLDocGlobalObject(nsXBLDocumentInfo *aGlobalObjectOwner)
--- a/js/jsd/jsd_high.cpp
+++ b/js/jsd/jsd_high.cpp
@@ -51,17 +51,19 @@ CreateJSDGlobal(JSContext *cx, const JSC
 
 /***************************************************************************/
 
 
 static const JSClass global_class = {
     "JSDGlobal", JSCLASS_GLOBAL_FLAGS |
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
     JS_PropertyStub,  JS_DeletePropertyStub,  JS_PropertyStub,  JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   global_finalize
+    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   global_finalize,
+    nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 static bool
 _validateUserCallbacks(JSD_UserCallbacks* callbacks)
 {
     return !callbacks ||
            (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
 }    
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1517,18 +1517,24 @@ GCMarker::processMarkStackTop(SliceBudge
         PushMarkStack(this, type);
 
         Shape *shape = obj->lastProperty();
         PushMarkStack(this, shape);
 
         /* Call the trace hook if necessary. */
         const Class *clasp = type->clasp();
         if (clasp->trace) {
+            // Global objects all have the same trace hook. That hook is safe without barriers
+            // if the gloal has no custom trace hook of it's own, or has been moved to a different
+            // compartment, and so can't have one.
             JS_ASSERT_IF(runtime->gcMode() == JSGC_MODE_INCREMENTAL &&
-                         runtime->gcIncrementalEnabled,
+                         runtime->gcIncrementalEnabled &&
+                         !(clasp->trace == JS_GlobalObjectTraceHook &&
+                           (!obj->compartment()->options().getTrace() ||
+                            !obj->isOwnGlobal())),
                          clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
             clasp->trace(this, obj);
         }
 
         if (!shape->isNative())
             return;
 
         unsigned nslots = obj->slotSpan();
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -11,17 +11,19 @@
 #include "jsfriendapi.h"
 
 using namespace JS;
 
 /* The class of the global object. */
 const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub, JS_PropertyStub,  JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+    nullptr, nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 template<typename T>
 static inline T *
 checkPtr(T *ptr)
 {
   if (! ptr)
     abort();
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -12,17 +12,22 @@ static const JSClass global_class = {
     "global",
     JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
-    JS_ConvertStub
+    JS_ConvertStub,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 static JSObject *trusted_glob = nullptr;
 static JSObject *trusted_fun = nullptr;
 
 static bool
 CallTrusted(JSContext *cx, unsigned argc, jsval *vp)
 {
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -20,17 +20,19 @@ GlobalResolve(JSContext *cx, JS::Handle<
     return JS_ResolveStandardClass(cx, obj, id, &resolved);
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-        GlobalEnumerate, GlobalResolve, JS_ConvertStub
+        GlobalEnumerate, GlobalResolve, JS_ConvertStub,
+        nullptr, nullptr, nullptr, nullptr,
+        JS_GlobalObjectTraceHook
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
     options.setVersion(JSVERSION_LATEST);
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
         return false;
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -224,17 +224,19 @@ class JSAPITest
     }
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
         static const JSClass c = {
             "global", JSCLASS_GLOBAL_FLAGS,
             JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-            JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+            JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr,
+            nullptr, nullptr, nullptr,
+            JS_GlobalObjectTraceHook
         };
         return &c;
     }
 
   protected:
     static bool
     print(JSContext *cx, unsigned argc, jsval *vp)
     {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2517,16 +2517,37 @@ JS_NewGlobalObject(JSContext *cx, const 
 
     if (hookOption == JS::FireOnNewGlobalHook)
         JS_FireOnNewGlobalObject(cx, global);
 
     return global;
 }
 
 JS_PUBLIC_API(void)
+JS_GlobalObjectTraceHook(JSTracer *trc, JSObject *global)
+{
+    JS_ASSERT(global->is<GlobalObject>());
+
+    // Off thread parsing and compilation tasks create a dummy global which is then
+    // merged back into the host compartment. Since it used to be a global, it will still
+    // have this trace hook, but it does not have a meaning relative to its new compartment.
+    // We can safely skip it.
+    if (!global->isOwnGlobal())
+        return;
+
+    // Trace the compartment for any GC things that should only stick around if we know the
+    // compartment is live.
+    global->compartment()->trace(trc);
+
+    JSTraceOp trace = global->compartment()->options().getTrace();
+    if (trace)
+        trace(trc, global);
+}
+
+JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext *cx, JS::HandleObject global)
 {
     // This hook is infallible, because we don't really want arbitrary script
     // to be able to throw errors during delicate global creation routines.
     // This infallibility will eat OOM and slow script, but if that happens
     // we'll likely run up into them again soon in a fallible context.
     Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
     Debugger::onNewGlobalObject(cx, globalObject);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2573,16 +2573,17 @@ class JS_PUBLIC_API(CompartmentOptions)
 
         Mode mode_;
     };
 
     explicit CompartmentOptions()
       : version_(JSVERSION_UNKNOWN)
       , invisibleToDebugger_(false)
       , mergeable_(false)
+      , traceGlobal_(nullptr)
       , singletonsAsTemplates_(true)
     {
         zone_.spec = JS::FreshZone;
     }
 
     JSVersion version() const { return version_; }
     CompartmentOptions &setVersion(JSVersion aVersion) {
         MOZ_ASSERT(aVersion != JSVERSION_UNKNOWN);
@@ -2623,25 +2624,34 @@ class JS_PUBLIC_API(CompartmentOptions)
 
     void setSingletonsAsValues() {
         singletonsAsTemplates_ = false;
     }
     bool getSingletonsAsTemplates() const {
         return singletonsAsTemplates_;
     };
 
+    CompartmentOptions &setTrace(JSTraceOp op) {
+        traceGlobal_ = op;
+        return *this;
+    }
+    JSTraceOp getTrace() const {
+        return traceGlobal_;
+    }
+
   private:
     JSVersion version_;
     bool invisibleToDebugger_;
     bool mergeable_;
     Override cloneSingletonsOverride_;
     union {
         ZoneSpecifier spec;
         void *pointer; // js::Zone* is not exposed in the API.
     } zone_;
+    JSTraceOp traceGlobal_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
     // templates, by making JSOP_OBJECT return a clone of the JSScript
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
 };
 
 JS_PUBLIC_API(CompartmentOptions &)
@@ -2676,16 +2686,27 @@ enum OnNewGlobalHookOption {
 };
 
 } /* namespace JS */
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, const JSClass *clasp, JSPrincipals *principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::CompartmentOptions &options = JS::CompartmentOptions());
+/*
+ * Spidermonkey does not have a good way of keeping track of what compartments should be marked on
+ * their own. We can mark the roots unconditionally, but marking GC things only relevant in live
+ * compartments is hard. To mitigate this, we create a static trace hook, installed on each global
+ * object, from which we can be sure the compartment is relevant, and mark it.
+ *
+ * It is still possible to specify custom trace hooks for global object classes. They can be
+ * provided via the CompartmentOptions passed to JS_NewGlobalObject.
+ */
+extern JS_PUBLIC_API(void)
+JS_GlobalObjectTraceHook(JSTracer *trc, JSObject *global);
 
 extern JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext *cx, JS::HandleObject global);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewObject(JSContext *cx, const JSClass *clasp, JS::Handle<JSObject*> proto,
              JS::Handle<JSObject*> parent);
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -514,16 +514,23 @@ JSCompartment::markCrossCompartmentWrapp
             Value referent = wrapper->private_();
             MarkValueRoot(trc, &referent, "cross-compartment wrapper");
             JS_ASSERT(referent == wrapper->private_());
         }
     }
 }
 
 void
+JSCompartment::trace(JSTracer *trc)
+{
+    // At the moment, this is merely ceremonial, but any live-compartment-only tracing should go
+    // here.
+}
+
+void
 JSCompartment::markRoots(JSTracer *trc)
 {
     JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
 
 #ifdef JS_ION
     if (jitCompartment_)
         jitCompartment_->mark(trc, this);
 #endif
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -323,16 +323,17 @@ struct JSCompartment
     void removeWrapper(js::WrapperMap::Ptr p) {
         crossCompartmentWrappers.remove(p);
     }
 
     struct WrapperEnum : public js::WrapperMap::Enum {
         WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
     };
 
+    void trace(JSTracer *trc);
     void markRoots(JSTracer *trc);
     bool isDiscardingJitCode(JSTracer *trc);
     void sweep(js::FreeOp *fop, bool releaseTypes);
     void sweepCrossCompartmentWrappers();
     void purge();
     void clearTables();
 
     bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1281,17 +1281,22 @@ NewObject(ExclusiveContext *cx, types::T
             return nullptr;
         obj = nobj;
     }
 
     /*
      * This will cancel an already-running incremental GC from doing any more
      * slices, and it will prevent any future incremental GCs.
      */
-    if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS)) {
+    bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook &&
+                                    !cx->compartment()->options().getTrace();
+    if (clasp->trace &&
+        !globalWithoutCustomTrace &&
+        !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
+    {
         if (!cx->shouldBeJSContext())
             return nullptr;
         JSRuntime *rt = cx->asJSContext()->runtime();
         rt->gcIncrementalEnabled = false;
 
 #ifdef DEBUG
         if (rt->gcMode() == JSGC_MODE_INCREMENTAL) {
             fprintf(stderr,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -542,16 +542,17 @@ class JSObject : public js::ObjectImpl
 
     /* Access the metadata on an object. */
     inline JSObject *getMetadata() const {
         return lastProperty()->getObjectMetadata();
     }
     static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata);
 
     inline js::GlobalObject &global() const;
+    inline bool isOwnGlobal() const;
 
     /* Remove the type (and prototype) or parent from a new object. */
     static inline bool clearType(JSContext *cx, js::HandleObject obj);
     static bool clearParent(JSContext *cx, js::HandleObject obj);
 
     /*
      * ES5 meta-object properties and operations.
      */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -644,16 +644,22 @@ JSObject::global() const
 #ifdef DEBUG
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
 #endif
     return *compartment()->maybeGlobal();
 }
 
+inline bool
+JSObject::isOwnGlobal() const
+{
+    return &global() == this;
+}
+
 namespace js {
 
 PropDesc::PropDesc(const Value &getter, const Value &setter,
                    Enumerability enumerable, Configurability configurable)
   : pd_(UndefinedValue()),
     value_(UndefinedValue()),
     get_(getter), set_(setter),
     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -164,17 +164,19 @@ js::CancelOffThreadIonCompile(JSCompartm
 #endif // JS_ION
 }
 
 static const JSClass workerGlobalClass = {
     "internal-worker-global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,
-    JS_ConvertStub,   nullptr
+    JS_ConvertStub,   nullptr,
+    nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const jschar *chars, size_t length, JSObject *scopeChain,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain),
     exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
@@ -302,16 +304,19 @@ js::StartOffThreadParseScript(JSContext 
     if (!EnsureWorkerThreadsInitialized(cx))
         return false;
 
     JS::CompartmentOptions compartmentOptions(cx->compartment()->options());
     compartmentOptions.setZone(JS::FreshZone);
     compartmentOptions.setInvisibleToDebugger(true);
     compartmentOptions.setMergeable(true);
 
+    // Don't falsely inherit the host's global trace hook.
+    compartmentOptions.setTrace(nullptr);
+
     JSObject *global = JS_NewGlobalObject(cx, &workerGlobalClass, nullptr,
                                           JS::FireOnNewGlobalHook, compartmentOptions);
     if (!global)
         return false;
 
     JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals);
 
     RootedObject obj(cx);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2672,17 +2672,19 @@ sandbox_resolve(JSContext *cx, HandleObj
 }
 
 static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,   JS_DeletePropertyStub,
     JS_PropertyStub,   JS_StrictPropertyStub,
     sandbox_enumerate, (JSResolveOp)sandbox_resolve,
-    JS_ConvertStub
+    JS_ConvertStub, nullptr,
+    nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 static JSObject *
 NewSandbox(JSContext *cx, bool lazy)
 {
     RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr,
                                             JS::DontFireOnNewGlobalHook));
     if (!obj)
@@ -4949,17 +4951,19 @@ global_resolve(JSContext *cx, HandleObje
     return true;
 }
 
 static const JSClass global_class = {
     "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     global_enumerate, (JSResolveOp) global_resolve,
-    JS_ConvertStub,   nullptr
+    JS_ConvertStub,   nullptr,
+    nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 static bool
 env_setProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
 {
 /* XXX porting may be easy, but these don't seem to supply setenv by default */
 #if !defined SOLARIS
     int rv;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -9,16 +9,17 @@
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "json.h"
 #include "jsprototypes.h"
 #include "jsweakmap.h"
+#include "jsworkers.h"
 
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
@@ -544,16 +545,17 @@ GlobalObject::initBuiltinConstructor(JSC
     types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     return true;
 }
 
 GlobalObject *
 GlobalObject::create(JSContext *cx, const Class *clasp)
 {
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
+    JS_ASSERT(clasp->trace == JS_GlobalObjectTraceHook);
 
     JSObject *obj = NewObjectWithGivenProto(cx, clasp, nullptr, nullptr, SingletonObject);
     if (!obj)
         return nullptr;
 
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
     cx->compartment()->initGlobal(*global);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -38,17 +38,19 @@ selfHosting_ErrorReporter(JSContext *cx,
     PrintError(cx, stderr, message, report, true);
 }
 
 static const JSClass self_hosting_global_class = {
     "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,
-    JS_ConvertStub,   nullptr
+    JS_ConvertStub,   nullptr,
+    nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
 };
 
 bool
 js::intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedValue val(cx, args[0]);
     RootedObject obj(cx, ToObject(cx, val));
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -680,17 +680,17 @@ sandbox_convert(JSContext *cx, HandleObj
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
 static const JSClass SandboxClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     JS_PropertyStub,   JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
-    nullptr, nullptr, nullptr, TraceXPCGlobal
+    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
 };
 
 static const JSFunctionSpec SandboxFunctions[] = {
     JS_FS("dump",    SandboxDump,    1,0),
     JS_FS("debug",   SandboxDebug,   1,0),
     JS_FS("importFunction", SandboxImport, 1,0),
     JS_FS_END
 };
@@ -1076,17 +1076,18 @@ xpc::CreateSandboxObject(JSContext *cx, 
     }
 
     JS::CompartmentOptions compartmentOptions;
     if (options.sameZoneAs)
         compartmentOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
     else
         compartmentOptions.setZone(JS::SystemZone);
 
-    compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger);
+    compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger)
+                      .setTrace(TraceXPCGlobal);
 
     RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass,
                                                      principal, compartmentOptions));
     if (!sandbox)
         return NS_ERROR_FAILURE;
 
     // Set up the wantXrays flag, which indicates whether xrays are desired even
     // for same-origin access.
--- a/js/xpconnect/src/XPCJSContextStack.cpp
+++ b/js/xpconnect/src/XPCJSContextStack.cpp
@@ -132,17 +132,17 @@ SafeFinalize(JSFreeOp *fop, JSObject* ob
     DestroyProtoAndIfaceCache(obj);
 }
 
 const JSClass xpc::SafeJSContextGlobalClass = {
     "global_for_XPCJSContextStack_SafeJSContext",
     XPCONNECT_GLOBAL_FLAGS,
     JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, SafeFinalize,
-    nullptr, nullptr, nullptr, TraceXPCGlobal
+    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
 };
 
 JSContext*
 XPCJSContextStack::GetSafeJSContext()
 {
     MOZ_ASSERT(mSafeJSContext);
     return mSafeJSContext;
 }
@@ -175,17 +175,18 @@ XPCJSContextStack::InitSafeJSContext()
     if (!mSafeJSContext)
         MOZ_CRASH();
     JSAutoRequest req(mSafeJSContext);
     ContextOptionsRef(mSafeJSContext).setNoDefaultCompartmentObject(true);
 
     JS_SetErrorReporter(mSafeJSContext, xpc::SystemErrorReporter);
 
     JS::CompartmentOptions options;
-    options.setZone(JS::SystemZone);
+    options.setZone(JS::SystemZone)
+           .setTrace(TraceXPCGlobal);
     mSafeJSContextGlobal = CreateGlobalObject(mSafeJSContext,
                                               &SafeJSContextGlobalClass,
                                               principal, options);
     if (!mSafeJSContextGlobal)
         MOZ_CRASH();
     JS_AddNamedObjectRoot(mSafeJSContext, &mSafeJSContextGlobal, "SafeJSContext global");
 
     // Note: make sure to set the private before calling
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -166,16 +166,17 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
     AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper));
     MOZ_ASSERT(si.get());
 
     // Finally, we get to the JSClass.
     const JSClass *clasp = si->GetJSClass();
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
     // Create the global.
+    aOptions.setTrace(XPCWrappedNative::Trace);
     RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
     if (!global)
         return NS_ERROR_FAILURE;
     XPCWrappedNativeScope *scope = GetCompartmentPrivate(global)->scope;
 
     // Immediately enter the global's compartment, so that everything else we
     // create ends up there.
     JSAutoCompartment ac(cx, global);
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -587,18 +587,18 @@ MarkWrappedNative(JSTracer *trc, JSObjec
     }
     MOZ_ASSERT(IS_WN_CLASS(clazz));
 
     XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
     if (wrapper && wrapper->IsValid())
         wrapper->TraceInside(trc);
 }
 
-static void
-XPC_WN_NoHelper_Trace(JSTracer *trc, JSObject *obj)
+/* static */ void
+XPCWrappedNative::Trace(JSTracer *trc, JSObject *obj)
 {
     MarkWrappedNative(trc, obj);
 }
 
 static bool
 XPC_WN_NoHelper_Resolve(JSContext *cx, HandleObject obj, HandleId id)
 {
     XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id);
@@ -673,17 +673,17 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
     XPC_WN_NoHelper_Resolve,           // resolve
     XPC_WN_Shared_Convert,             // convert
     XPC_WN_NoHelper_Finalize,          // finalize
 
     /* Optionally non-null members start here. */
     nullptr,                         // call
     nullptr,                         // construct
     nullptr,                         // hasInstance
-    XPC_WN_NoHelper_Trace,          // trace
+    XPCWrappedNative::Trace,         // trace
     JS_NULL_CLASS_SPEC,
 
     // ClassExtension
     {
         nullptr, // outerObject
         nullptr, // innerObject
         nullptr, // iteratorObject
         true,   // isWrappedNative
@@ -1203,17 +1203,20 @@ XPCNativeScriptableShared::PopulateJSCla
     if (mFlags.WantCall())
         mJSClass.base.call = XPC_WN_Helper_Call;
     if (mFlags.WantConstruct())
         mJSClass.base.construct = XPC_WN_Helper_Construct;
 
     if (mFlags.WantHasInstance())
         mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
 
-    mJSClass.base.trace = XPC_WN_NoHelper_Trace;
+    if (mFlags.IsGlobalObject())
+        mJSClass.base.trace = JS_GlobalObjectTraceHook;
+    else
+        mJSClass.base.trace = XPCWrappedNative::Trace;
 
     if (mFlags.WantOuterObject())
         mJSClass.base.ext.outerObject = XPC_WN_OuterObject;
 
     mJSClass.base.ext.isWrappedNative = true;
 }
 
 /***************************************************************************/
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2128,16 +2128,18 @@ public:
         // normally somebody else is doing that. Be careful not to trace the
         // bogus INVALID_OBJECT value we can have during init, though.
         if (mFlatJSObject) {
             JS_CallTenuredObjectTracer(trc, &mFlatJSObject,
                                        "XPCWrappedNative::mFlatJSObject");
         }
     }
 
+    static void Trace(JSTracer *trc, JSObject *obj);
+
     void AutoTrace(JSTracer *trc) {
         TraceSelf(trc);
     }
 
 #ifdef DEBUG
     void ASSERT_SetsNotMarked() const
         {mSet->ASSERT_NotMarked();
          if (HasProto()){GetProto()->ASSERT_SetNotMarked();}}
--- a/netwerk/base/src/ProxyAutoConfig.cpp
+++ b/netwerk/base/src/ProxyAutoConfig.cpp
@@ -580,17 +580,19 @@ private:
     return NS_OK;
   }
 };
 
 const JSClass JSRuntimeWrapper::sGlobalClass = {
   "PACResolutionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+  nullptr, nullptr, nullptr, nullptr,
+  JS_GlobalObjectTraceHook
 };
 
 nsresult
 ProxyAutoConfig::Init(const nsCString &aPACURI,
                       const nsCString &aPACScript)
 {
   mPACURI = aPACURI;
   mPACScript = sPacUtils;
--- a/tools/profiler/SaveProfileTask.cpp
+++ b/tools/profiler/SaveProfileTask.cpp
@@ -56,17 +56,19 @@ SaveProfileTask::Run() {
     return NS_ERROR_FAILURE;
   }
 
   {
     JSAutoRequest ar(cx);
     static const JSClass c = {
       "global", JSCLASS_GLOBAL_FLAGS,
       JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-      JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+      JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+      nullptr, nullptr, nullptr, nullptr,
+      JS_GlobalObjectTraceHook
     };
     JSObject *obj = JS_NewGlobalObject(cx, &c, nullptr, JS::FireOnNewGlobalHook);
 
     std::ofstream stream;
     stream.open(tmpPath.get());
     if (stream.is_open()) {
       JSAutoCompartment autoComp(cx, obj);
       JSObject* profileObj = profiler_get_profile_jsobject(cx);
--- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -87,17 +87,19 @@ RunTest(JSRuntime* rt, JSContext* cx)
 }
 
 static void
 CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx)
 {
   static const JSClass GlobalClass = {
     "global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+    nullptr, nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
   };
 
   JS::CompartmentOptions options;
   options.setVersion(JSVERSION_LATEST);
   JS::RootedObject global(cx);
   global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
   ASSERT_TRUE(global != nullptr);